diff mbox

[8/9] Introduce class function_reader (v4)

Message ID 1478898935-46932-9-git-send-email-dmalcolm@redhat.com
State New
Headers show

Commit Message

David Malcolm Nov. 11, 2016, 9:15 p.m. UTC
Changed in v4:
- error-handling changes split out into a separate patch
- rewritten the loader to use the new "compact" dump format
- support for reuse_rtx in loader
- handling of params, DECL_RTL and DECL_RTL_INCOMING
- moved target-dependent selftests to target-specific code
(aarch64.c and i386.c)

Link to earlier version of the patch:
  https://gcc.gnu.org/ml/gcc-patches/2016-10/msg00267.html

gcc/ChangeLog:
	* Makefile.in (OBJS): Add read-md.o, read-rtl.o,
	read-rtl-function.o, and selftest-rtl.o.
	* config/aarch64/aarch64.c: Include selftest.h and
	selftest-rtl.h.
	(selftest::aarch64_test_loading_full_dump): New function.
	(selftest::aarch64_run_selftests): New function.
	(TARGET_RUN_TARGET_SELFTESTS): Wire it up to
	selftest::aarch64_run_selftests.
	* config/i386/i386.c
	(selftest::ix86_test_loading_dump_fragment_1): New function.
	(selftest::ix86_test_loading_call_insn): New function.
	(selftest::ix86_test_loading_full_dump): New function.
	(selftest::ix86_test_loading_unspec): New function.
	(selftest::ix86_run_selftests): Call the new functions.
	* emit-rtl.c (maybe_set_max_label_num): New function.
	* emit-rtl.h (maybe_set_max_label_num): 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)).
	* gensupport.c (gen_reader::gen_reader): Pass "false"
	for new "compact" param of rtx_reader.
	* print-rtl.c (rtx_writer::print_rtx_operand): Print "(nil)"
	rather than an empty string for NULL strings.
	* read-md.c: Potentially include config.h rather than bconfig.h.
	(md_reader::read_name): Rename to...
	(md_reader::read_name_1): ...this, adding "out_loc" param,
	and converting "missing name or number" to returning false, rather
	than failing.
	(md_reader::read_name): Reimplement in terms of read_name_1.
	(md_reader::read_name_or_nil): New function.
	(md_reader::read_string): Handle "(nil)" by returning NULL.
	(md_reader::md_reader): Add new param "compact".
	* read-md.h (md_reader::md_reader): Add new param "compact".
	(md_reader::is_compact): New accessor.
	(md_reader::read_name): Convert return type from void to
	file_location.
	(md_reader::read_name_or_nil): New decl.
	(md_reader::read_name_1): New decl.
	(md_reader::m_compact): New field.
	(noop_reader::noop_reader): Pass "false" for new "compact" param
	of rtx_reader.
	(rtx_reader::rtx_reader): Add new "compact" param.
	(rtx_reader::read_rtx_operand): Make virtual and convert return
	type from void to rtx.
	(rtx_reader::read_until): New decl.
	(rtx_reader::handle_any_trailing_information): New virtual
	function.
	(rtx_reader::postprocess): New virtual function.
	(rtx_reader::m_in_call_function_usage): New field.
	(rtx_reader::m_reuse_rtx_by_id): New field.
	* 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, memmodel.h, and emit-rtl.h.
	(one_time_initialization): New function.
	(find_code): Handle insn codes in compact dumps.
	(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.
	(md_reader::read_mapping): Likewise.
	(add_define_attr_for_define_subst): Likewise.
	(add_define_subst_attr): Likewise.
	(read_subst_mapping): Likewise.
	(check_code_iterator): Likewise.
	(rtx_reader::read_rtx): Likewise.  Move one-time initialization
	logic to...
	(one_time_initialization): New function.
	(rtx_reader::read_until): New method.
	(read_flags): New function.
	(parse_reg_note_name): New function.
	(rtx_reader::read_rtx_code): Initialize "iterator" to NULL.
	Call one_time_initialization.  Handle reuse_rtx ids.
	Wrap iterator lookup within #ifdef GENERATOR_FILE.
	Add parsing support for RTL dumps, mirroring the special-cases in
	print_rtx, by calling read_flags, reading REG_NOTE names, INSN_UID
	values, and calling handle_any_trailing_information.
	(rtx_reader::read_rtx_operand): Convert return type from void
	to rtx, returning return_rtx.  Handle case 'e'.  When on host,
	reallocate XSTR and XTMPL fields in the GC-managed heap.
	(rtx_reader::read_nested_rtx):  Handle dumps in which trailing
	 "(nil)" values were omitted.  Call the postprocess vfunc on the
	return_rtx.
	(rtx_reader::rtx_reader): Add new "compact" param and pass to base
	class ctor.  Initialize m_in_call_function_usage.
	* rtl-tests.c (selftest::test_uncond_jump): Call
	set_new_first_and_last_insn.
	* rtl.h (read_rtx): Wrap decl with #ifdef GENERATOR_FILE.
	* selftest-rtl.c: New file.
	* selftest-rtl.h (class selftest::rtl_dump_test): New class.
	(selftest::get_insn_by_uid): New decl.
	(selftest::verify_three_block_rtl_cfg): New decl.
	* selftest-run-tests.c (selftest::run_tests): Call
	read_rtl_function_c_tests.
	* selftest.h  (selftest::read_rtl_function_c_tests): New decl.
	* tree-dfa.c (ssa_default_def): Return NULL_TREE for rtl function
	dumps.

gcc/testsuite/ChangeLog:
	* selftests/aarch64: New subdirectory.
	* selftests/aarch64/times-two.rtl: New file.
	* selftests/asr_div1.rtl: New file.
	* selftests/bb-index.rtl: New file.
	* selftests/cfg-test.rtl: New file.
	* selftests/const-int.rtl: New file.
	* selftests/example-labels.rtl: New file.
	* selftests/insn-with-mode.rtl: New file.
	* selftests/jump-to-label-ref.rtl: New file.
	* selftests/jump-to-return.rtl: New file.
	* selftests/jump-to-simple-return.rtl: New file.
	* selftests/note-insn-deleted.rtl: New file.
	* selftests/note_insn_basic_block.rtl: New file.
	* selftests/simple-cse.rtl: New file.
	* selftests/symbol-ref.rtl: New file.
	* selftests/x86_64: New subdirectory.
	* selftests/x86_64/call-insn.rtl: New file.
	* selftests/x86_64/copy-hard-reg-into-frame.rtl: New file.
	* selftests/x86_64/times-two.rtl: New file.
	* selftests/x86_64/unspec.rtl: New file.
---
 gcc/Makefile.in                                    |    4 +
 gcc/config/aarch64/aarch64.c                       |   49 +
 gcc/config/i386/i386.c                             |  207 ++
 gcc/emit-rtl.c                                     |   13 +
 gcc/emit-rtl.h                                     |    2 +
 gcc/function-tests.c                               |    2 +-
 gcc/function.c                                     |    3 +-
 gcc/gensupport.c                                   |    2 +-
 gcc/print-rtl.c                                    |    2 +-
 gcc/read-md.c                                      |   59 +-
 gcc/read-md.h                                      |   29 +-
 gcc/read-rtl-function.c                            | 2124 ++++++++++++++++++++
 gcc/read-rtl-function.h                            |   36 +
 gcc/read-rtl.c                                     |  263 ++-
 gcc/rtl-tests.c                                    |    1 +
 gcc/rtl.h                                          |    2 +
 gcc/selftest-rtl.c                                 |   82 +
 gcc/selftest-rtl.h                                 |   19 +
 gcc/selftest-run-tests.c                           |    1 +
 gcc/selftest.h                                     |    1 +
 gcc/testsuite/selftests/aarch64/times-two.rtl      |   36 +
 gcc/testsuite/selftests/asr_div1.rtl               |   24 +
 gcc/testsuite/selftests/bb-index.rtl               |    8 +
 gcc/testsuite/selftests/cfg-test.rtl               |   37 +
 gcc/testsuite/selftests/const-int.rtl              |   20 +
 gcc/testsuite/selftests/example-labels.rtl         |    8 +
 gcc/testsuite/selftests/insn-with-mode.rtl         |    7 +
 gcc/testsuite/selftests/jump-to-label-ref.rtl      |   17 +
 gcc/testsuite/selftests/jump-to-return.rtl         |   11 +
 gcc/testsuite/selftests/jump-to-simple-return.rtl  |   11 +
 gcc/testsuite/selftests/note-insn-deleted.rtl      |    5 +
 gcc/testsuite/selftests/note_insn_basic_block.rtl  |    9 +
 gcc/testsuite/selftests/simple-cse.rtl             |   16 +
 gcc/testsuite/selftests/symbol-ref.rtl             |   13 +
 gcc/testsuite/selftests/x86_64/call-insn.rtl       |   17 +
 .../selftests/x86_64/copy-hard-reg-into-frame.rtl  |   15 +
 gcc/testsuite/selftests/x86_64/times-two.rtl       |   51 +
 gcc/testsuite/selftests/x86_64/unspec.rtl          |   20 +
 gcc/tree-dfa.c                                     |    5 +
 39 files changed, 3200 insertions(+), 31 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/testsuite/selftests/aarch64/times-two.rtl
 create mode 100644 gcc/testsuite/selftests/asr_div1.rtl
 create mode 100644 gcc/testsuite/selftests/bb-index.rtl
 create mode 100644 gcc/testsuite/selftests/cfg-test.rtl
 create mode 100644 gcc/testsuite/selftests/const-int.rtl
 create mode 100644 gcc/testsuite/selftests/example-labels.rtl
 create mode 100644 gcc/testsuite/selftests/insn-with-mode.rtl
 create mode 100644 gcc/testsuite/selftests/jump-to-label-ref.rtl
 create mode 100644 gcc/testsuite/selftests/jump-to-return.rtl
 create mode 100644 gcc/testsuite/selftests/jump-to-simple-return.rtl
 create mode 100644 gcc/testsuite/selftests/note-insn-deleted.rtl
 create mode 100644 gcc/testsuite/selftests/note_insn_basic_block.rtl
 create mode 100644 gcc/testsuite/selftests/simple-cse.rtl
 create mode 100644 gcc/testsuite/selftests/symbol-ref.rtl
 create mode 100644 gcc/testsuite/selftests/x86_64/call-insn.rtl
 create mode 100644 gcc/testsuite/selftests/x86_64/copy-hard-reg-into-frame.rtl
 create mode 100644 gcc/testsuite/selftests/x86_64/times-two.rtl
 create mode 100644 gcc/testsuite/selftests/x86_64/unspec.rtl

Comments

Bernd Schmidt Nov. 23, 2016, 8:15 p.m. UTC | #1
On 11/11/2016 10:15 PM, David Malcolm wrote:

> +static void
> +aarch64_test_loading_full_dump ()
> +{
> +  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("aarch64/times-two.rtl"));
> +
> +  ASSERT_STREQ ("times_two", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
> +
> +  rtx_insn *insn_1 = get_insn_by_uid (1);
> +  ASSERT_EQ (NOTE, GET_CODE (insn_1));
> +
> +  rtx_insn *insn_15 = get_insn_by_uid (15);
> +  ASSERT_EQ (INSN, GET_CODE (insn_15));
> +  ASSERT_EQ (USE, GET_CODE (PATTERN (insn_15)));
> +
> +  /* Verify crtl->return_rtx.  */
> +  ASSERT_EQ (REG, GET_CODE (crtl->return_rtx));
> +  ASSERT_EQ (0, REGNO (crtl->return_rtx));
> +  ASSERT_EQ (SImode, GET_MODE (crtl->return_rtx));
> +}

The problem I'm having with this patch, and why I've not really 
commented on the latter parts of the series, is that I was hoping the 
tests would be more self-contained. For transformations I had mentioned 
the idea of having both before and after dumps.

Having a few test files with C code to verify them is probably not a big 
deal, but I wonder whether this is going to be the norm rather than the 
exception, and whether there are better ways of doing it. Maybe there 
needs to be a way of annotating the input RTL to tell the compiler what 
kinds of tests to run on it.


Bernd
David Malcolm Nov. 23, 2016, 8:46 p.m. UTC | #2
On Wed, 2016-11-23 at 21:15 +0100, Bernd Schmidt wrote:
> On 11/11/2016 10:15 PM, David Malcolm wrote:
> 
> > +static void
> > +aarch64_test_loading_full_dump ()
> > +{
> > +  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("aarch64/times
> > -two.rtl"));
> > +
> > +  ASSERT_STREQ ("times_two", IDENTIFIER_POINTER (DECL_NAME (cfun
> > ->decl)));
> > +
> > +  rtx_insn *insn_1 = get_insn_by_uid (1);
> > +  ASSERT_EQ (NOTE, GET_CODE (insn_1));
> > +
> > +  rtx_insn *insn_15 = get_insn_by_uid (15);
> > +  ASSERT_EQ (INSN, GET_CODE (insn_15));
> > +  ASSERT_EQ (USE, GET_CODE (PATTERN (insn_15)));
> > +
> > +  /* Verify crtl->return_rtx.  */
> > +  ASSERT_EQ (REG, GET_CODE (crtl->return_rtx));
> > +  ASSERT_EQ (0, REGNO (crtl->return_rtx));
> > +  ASSERT_EQ (SImode, GET_MODE (crtl->return_rtx));
> > +}
> 
> The problem I'm having with this patch, and why I've not really 
> commented on the latter parts of the series, is that I was hoping the
> tests would be more self-contained.

The tests in this patch (8/9) are more about verifying that the loader
is sane: they load a dump, and then assert various properties of it, to
ensure that we're loading things correctly.  I moved the example dumps
out of the .c files (using locate_file) to avoid having to embed them
in the .c file and escape things (which people objected to in an
earlier version of the kit).

> For transformations I had mentioned 
> the idea of having both before and after dumps.
> 
> Having a few test files with C code to verify them is probably not a
> big 
> deal, but I wonder whether this is going to be the norm rather than
> the 
> exception, and whether there are better ways of doing it.

These will be the exception; they're really just about verifying that
the loader is working correctly.

In a previous version of the kit there were indeed selftests that
loaded a dump and attempted to run parts of a pass on it.  I've deleted
all of those style of selftest.

> Maybe there 
> needs to be a way of annotating the input RTL to tell the compiler
> what 
> kinds of tests to run on it.

The "real" tests are in patch 9 of 9, and use DejaGnu directives
(scanning dumps, and "dg-do run" to actually run the generated code).
Bernd Schmidt Dec. 1, 2016, 2:40 p.m. UTC | #3
On 11/11/2016 10:15 PM, David Malcolm wrote:
>  #include "gt-aarch64.h"
> diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> index 6c608e0..0dda786 100644
> --- a/gcc/config/i386/i386.c
> +++ b/gcc/config/i386/i386.c

I think we should separate out the target specific tests so as to give 
port maintainers a chance to comment on them separately.

> diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
> index 50cd388..179a91f 100644
> --- a/gcc/emit-rtl.c
> +++ b/gcc/emit-rtl.c
> @@ -1371,6 +1371,19 @@ maybe_set_first_label_num (rtx_code_label *x)
>    if (CODE_LABEL_NUMBER (x) < first_label_num)
>      first_label_num = CODE_LABEL_NUMBER (x);
>  }
> +
> +/* For use by the RTL function loader, when mingling with normal
> +   functions.

Not sure what this means.


>        if (str == 0)
> -	fputs (" \"\"", m_outfile);
> +	fputs (" (nil)", m_outfile);
>        else
>  	fprintf (m_outfile, " (\"%s\")", str);
>        m_sawclose = 1;

What does this affect?

>  /* Global singleton; constrast with md_reader_ptr above.  */
> diff --git a/gcc/read-rtl-function.c b/gcc/read-rtl-function.c
> new file mode 100644
> index 0000000..ff6c808
> --- /dev/null
> +++ b/gcc/read-rtl-function.c
> @@ -0,0 +1,2124 @@
> +/* 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
> +#include <mpfr.h>

Please double-check all these includes whether they are necessary.

> +
> +/* Fix up a NOTE_INSN_BASIC_BLOCK based on an integer block ID.  */
> +
> +void
> +fixup_note_insn_basic_block::apply (function_reader */*reader*/) const

Lose the /*reader*/, probably.

> +
> +/* Implementation of rtx_reader::handle_unknown_directive.
> +
> +   Require a top-level "function" elements, as emitted by
> +   print_rtx_function, and parse it.  */

"element"?

> +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 ();

Do we expect to change this? I'd just get rid of the if (0), at least 
for now.

> +    /* 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;
> +  }

Does it do anything beside call finalize_function in that state? If 
that's all you need, just call it directly.

> +
> +  /* Parse DECL_RTL.  */
> +  {
> +    require_char_ws ('(');
> +    require_word_ws ("DECL_RTL");
> +    DECL_WRTL_CHECK (t_param)->decl_with_rtl.rtl = parse_rtx ();
> +    require_char_ws (')');
> +  }

Spurious { } blocks.

> +  if (0)
> +    fprintf (stderr, "parse_edge: %i flags 0x%x \n",
> +	     other_bb_idx, flags);

Remove this.
> +  /* For now, only process the (edge-from) to this BB, and (edge-to)
> +     that go to the exit block; we don't yet verify that the edge-from
> +     and edge-to directives are consistent.  */

That's probably worth a FIXME.

> +  if (rtx_code_label *label = dyn_cast <rtx_code_label *> (insn))
> +    maybe_set_max_label_num (label);

I keep forgetting why dyn_cast instead of as_a?

> +    case 'e':
> +      {
> +	if (idx == 7 && CALL_P (return_rtx))
> +	  {
> +	    m_in_call_function_usage = true;
> +	    return rtx_reader::read_rtx_operand (return_rtx, idx);
> +	    m_in_call_function_usage = false;
> +	  }
> +	else
> +	  return rtx_reader::read_rtx_operand (return_rtx, idx);
> +      }
> +      break;

Unnecessary { } blocks in several places.

> +
> +    case 'w':
> +      {
> +	if (!is_compact ())
> +	  {
> +	    /* Strip away the redundant hex dump of the value.  */
> +	    require_char_ws ('[');
> +	    read_name (&name);
> +	    require_char_ws (']');
> +	  }
> +      }
> +      break;

Here too.

> +
> +/* Special-cased handling of codes 'i' and 'n' for reading function
> +   dumps.  */
> +
> +void
> +function_reader::read_rtx_operand_i_or_n (rtx return_rtx, int idx,
> +					  char format_char)

Document arguments (everywhere). I think return_rtx (throughout these 
functions) is a poor name that can cause confusion because it seems to 
imply a (return).

> +
> +      /* Possibly wrote:
> +	 print_node_brief (outfile, "", SYMBOL_REF_DECL (in_rtx),
> +			   dump_flags);  */

???

> +	  /* Skip the content for now.  */

Does this relate to the above? Please clarify the comments.

> +      case CODE_LABEL:
> +	{
> +	  /* Assume that LABEL_NUSES was not dumped.  */
> +	  /* TODO: parse LABEL_KIND.  */

Unnecessary { }.

> +  if (0 == strcmp (desc, "<retval>"))
> +    {
> +      return DECL_RESULT (fndecl);
> +    }
> +
> +  /* Search within function parms.  */
> +  for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
> +    {
> +      if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (arg))) == 0)
> +	return arg;
> +    }

No { } around single statements, both cases.

> +  /* Not found?  Create it.
> +     This allows mimicing of real data but avoids having to specify

"mimicking".

> +rtx
> +function_reader::consolidate_singletons (rtx x)
> +{
> +  if (!x)
> +    return x;
> +
> + switch (GET_CODE (x))

Formatting.

> +    {
> +    /* FIXME: do we need to check for VOIDmode for these?  */

Don't see why we would.

> +    case CONST_INT:
> +      return gen_rtx_CONST_INT (GET_MODE (x), INTVAL (x));

Ugh, really? Can't this be done earlier?

> +
> +  add_fixup_source_location (loc, insn, operand_idx,
> +			     filename, atoi(name.string));

Space before open paren.

> +  /* Verify lookup of hard registers.  */
> +#ifdef GCC_AARCH64_H
> +  ASSERT_EQ (0, lookup_reg_by_dump_name ("x0"));
> +  ASSERT_EQ (1, lookup_reg_by_dump_name ("x1"));
> +#endif
> +#ifdef I386_OPTS_H
> +  ASSERT_EQ (0, lookup_reg_by_dump_name ("ax"));
> +  ASSERT_EQ (1, lookup_reg_by_dump_name ("dx"));
> +#endif

Same as before, please don't do this here.

> +/* Verify that the src and dest indices and flags of edge E are as
> +   expected, using LOC as the effective location when reporting
> +   failures.  */

Again, please document all args (everywhere).
> +
> +static void
> +test_loading_dump_fragment_1 ()
> +{
> +  // TODO: filter on target?
> +  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("asr_div1.rtl"));

This is in a generic file, isn't it? Yeah, I don't think we want to load 
any RTL from here. Split out such selftests into a separate patch so we 
can figure out what to do with them.

> +
> +  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 ^^^

Why is this a fixme and not just done that way (several instances)?
> +
> +/* An optional policy class for class function_reader.  */
> +
> +struct function_reader_policy
> +{
> +  function_reader_policy ()
> +  {
> +  }
> +};
> +
> +extern bool read_rtl_function_body (int argc, const char **argv,
> +				    bool (*parse_opt) (const char *),
> +				    function_reader_policy *policy);

The policy seems completely unused, please remove. Also, can't this 
single declaration go into an existing header?

> +  /* Handle insn codes in compact dumps.  In such dumps, the code of insns
> +     is prefixed with "c", giving "cinsn", "cnote" etc, and CODE_LABEL is
> +     special-cased as "clabel".  */
> +  if (name[0] == 'c')
> +    {
> +      for (i = 0; i < NUM_RTX_CODE; i++)
> +	if (strcmp (GET_RTX_NAME (i), name + 1) == 0)
> +	  return i;

I'd rather check specifically for the ones we expect, to avoid surprises.

> +#ifdef GENERATOR_FILE

There's a lot of this. Is there a clean split where stuff could be moved 
into a separate file instead?

> @@ -1103,13 +1233,39 @@ rtx_reader::read_rtx_code (const char *code_name)
>        rtx value;		/* Value of this node.  */
>      };
>
> +  one_time_initialization ();

Isn't there a better place to call this?

> +
> +  /* Use the format_ptr to parse the various operands of this rtx.
> +     read_rtx_operand is a vfunc, allowing the parser to vary between
> +     parsing .md files and parsing .rtl dumps; the function_reader subclass
> +     overrides the defaults when loading RTL dumps, to handle the
> +     various extra attributes emitted by print_rtx.  */

Not sure that documentation is really necessary at this point. If 
someone is looking for the definition of read_rtx_operand they'll figure 
out it's a virtual function anyway.

>    for (int idx = 0; format_ptr[idx] != 0; idx++)
> -    read_rtx_operand (return_rtx, idx);
> +    return_rtx = read_rtx_operand (return_rtx, idx);
> +
> +  /* Call a vfunc to handle the various additional information that
> +     print-rtl.c can write after the regular fields; does nothing when
> +     parsing .md files.  */
> +  handle_any_trailing_information (return_rtx);

Same here really.
> +
> +	/* "stringbuf" was allocated within string_obstack and thus has
> +	   the its lifetime restricted to that of the rtx_reader.  This is
> +	   OK for the generator programs, but for non-generator programs,
> +	   XSTR and XTMPL fields are meant to be allocated in the GC-managed
> +	   heap.  Hence we need to allocate a copy in the GC-managed heap
> +	   for the non-generator case.  */
> +	const char *string_ptr = stringbuf;
> +#ifndef GENERATOR_FILE
> +	string_ptr = ggc_strdup (stringbuf);
> +#endif /* #ifndef GENERATOR_FILE */

Encapsulate in a virtual finalize_string perhaps?


Bernd
David Malcolm Dec. 1, 2016, 9:43 p.m. UTC | #4
On Thu, 2016-12-01 at 15:40 +0100, Bernd Schmidt wrote:

Thanks for looking at this.

> On 11/11/2016 10:15 PM, David Malcolm wrote:
> >  #include "gt-aarch64.h"
> > diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
> > index 6c608e0..0dda786 100644
> > --- a/gcc/config/i386/i386.c
> > +++ b/gcc/config/i386/i386.c
> 
> I think we should separate out the target specific tests so as to
> give 
> port maintainers a chance to comment on them separately.

Done.

> > diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
> > index 50cd388..179a91f 100644
> > --- a/gcc/emit-rtl.c
> > +++ b/gcc/emit-rtl.c
> > @@ -1371,6 +1371,19 @@ maybe_set_first_label_num (rtx_code_label
> > *x)
> >    if (CODE_LABEL_NUMBER (x) < first_label_num)
> >      first_label_num = CODE_LABEL_NUMBER (x);
> >  }
> > +
> > +/* For use by the RTL function loader, when mingling with normal
> > +   functions.
> 
> Not sure what this means.

This is for patch 9 which adds __RTL support to cc1, so that we can
have testcases in which some function bodies are in RTL form, and
others are in normal C form.

The issue is that the CODE_LABEL_NUMBER values in the RTL dump make it
into the generated assembler, and must be unique within the .s file. 
 Hence we need to update first_label_num so that any new labels created
(e.g. when expanding pure C functions) don't clash with the ones from
the dump.

I can simply drop that first sentence; the followup probably is
sufficient:

   /* Ensure that label_num is greater than the label num of X, to
      avoid duplicate labels in the generated assembler.  */

> 
> >        if (str == 0)
> > -	fputs (" \"\"", m_outfile);
> > +	fputs (" (nil)", m_outfile);
> >        else
> >  	fprintf (m_outfile, " (\"%s\")", str);
> >        m_sawclose = 1;
> 
> What does this affect?

This affects things like the LABEL_NAME of CODE_LABEL instances.

The hunk means that we preserve NULL string values as NULL, rather than
them becoming the empty string on a round trip.

> >  /* Global singleton; constrast with md_reader_ptr above.  */
> > diff --git a/gcc/read-rtl-function.c b/gcc/read-rtl-function.c
> > new file mode 100644
> > index 0000000..ff6c808
> > --- /dev/null
> > +++ b/gcc/read-rtl-function.c
> > @@ -0,0 +1,2124 @@
> > +/* 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
> > +#include <mpfr.h>
> 
> Please double-check all these includes whether they are necessary.

Thanks; yes, quite a few were unnecessary.  Fixed.

> > +
> > +/* Fix up a NOTE_INSN_BASIC_BLOCK based on an integer block ID. 
> >  */
> > +
> > +void
> > +fixup_note_insn_basic_block::apply (function_reader */*reader*/)
> > const
> 
> Lose the /*reader*/, probably.

Done.

> > +
> > +/* Implementation of rtx_reader::handle_unknown_directive.
> > +
> > +   Require a top-level "function" elements, as emitted by
> > +   print_rtx_function, and parse it.  */
> 
> "element"?

Converted to "directive" (I've been trying to use "directive" rather
than "element" throughout).

> > +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 ();
> 
> Do we expect to change this? I'd just get rid of the if (0), at least
> for now.

Patch 9 adds some logic for switching the hooks as we go through the
various passes, so that "startwith" works properly, but here we just
need it to reflect the state of the hooks coming out of "expand", so
I've changed it to:

  /* We start in cfgrtl mode, rather than cfglayout mode.  */
  rtl_register_cfg_hooks ();

> > +    /* 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;
> > +  }
> 
> Does it do anything beside call finalize_function in that state? If 
> that's all you need, just call it directly.

There's some logic after the switch on state for calling
lang_hooks.eh_personality and setting DECL_FUNCTION_PERSONALITY, but I
think if we want to support that, we'd add it as additional dumped
data.

I've updated things to call finalize_function directly, removing the
messing about with the symtab state.

> > +
> > +  /* Parse DECL_RTL.  */
> > +  {
> > +    require_char_ws ('(');
> > +    require_word_ws ("DECL_RTL");
> > +    DECL_WRTL_CHECK (t_param)->decl_with_rtl.rtl = parse_rtx ();
> > +    require_char_ws (')');
> > +  }
> 
> Spurious { } blocks.

Removed.

> > +  if (0)
> > +    fprintf (stderr, "parse_edge: %i flags 0x%x \n",
> > +	     other_bb_idx, flags);
> 
> Remove this.

Removed.

> > +  /* For now, only process the (edge-from) to this BB, and (edge
> > -to)
> > +     that go to the exit block; we don't yet verify that the edge
> > -from
> > +     and edge-to directives are consistent.  */
> 
> That's probably worth a FIXME.

Added.

> > +  if (rtx_code_label *label = dyn_cast <rtx_code_label *> (insn))
> > +    maybe_set_max_label_num (label);
> 
> I keep forgetting why dyn_cast instead of as_a?

as_a requires that the cast be valid; dyn_cast, determines if the cast
is valid, and returns NULL if it isn't valid, allowing uses within a
conditional like the one here.

> > +    case 'e':
> > +      {
> > +	if (idx == 7 && CALL_P (return_rtx))
> > +	  {
> > +	    m_in_call_function_usage = true;
> > +	    return rtx_reader::read_rtx_operand (return_rtx, idx);
> > +	    m_in_call_function_usage = false;
> > +	  }
> > +	else
> > +	  return rtx_reader::read_rtx_operand (return_rtx, idx);
> > +      }
> > +      break;
> 
> Unnecessary { } blocks in several places.

Fixed.

> > +
> > +    case 'w':
> > +      {
> > +	if (!is_compact ())
> > +	  {
> > +	    /* Strip away the redundant hex dump of the value.  */
> > +	    require_char_ws ('[');
> > +	    read_name (&name);
> > +	    require_char_ws (']');
> > +	  }
> > +      }
> > +      break;
> 
> Here too.

Fixed.

> > +
> > +/* Special-cased handling of codes 'i' and 'n' for reading
> > function
> > +   dumps.  */
> > +
> > +void
> > +function_reader::read_rtx_operand_i_or_n (rtx return_rtx, int idx,
> > +					  char format_char)
> 
> Document arguments (everywhere). I think return_rtx (throughout these
> functions) is a poor name that can cause confusion because it seems
> to 
> imply a (return).

I've renamed it to "x" throughout.  I've gone through and documented
arguments and return values throughout (I hope).  In doing so, I found
 extra_parsing_for_operand_code_0's return value to be redundant, so I
made it "void".

> > +
> > +      /* Possibly wrote:
> > +	 print_node_brief (outfile, "", SYMBOL_REF_DECL (in_rtx),
> > +			   dump_flags);  */
> 
> ???

I've rewritten the comment to read:

      /* If X had a non-NULL SYMBOL_REF_DECL,
	 rtx_writer::print_rtx_operand_code_0 would have dumped it
	 using print_node_brief.
	 Skip the content for now.  */

Hopefully that's clearer.

> > +	  /* Skip the content for now.  */
> 
> Does this relate to the above? Please clarify the comments.

Indeed; I've merged it into the above comment.
> 
> > +      case CODE_LABEL:
> > +	{
> > +	  /* Assume that LABEL_NUSES was not dumped.  */
> > +	  /* TODO: parse LABEL_KIND.  */
> 
> Unnecessary { }.

Fixed.

> > +  if (0 == strcmp (desc, "<retval>"))
> > +    {
> > +      return DECL_RESULT (fndecl);
> > +    }
> > +
> > +  /* Search within function parms.  */
> > +  for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN
> > (arg))
> > +    {
> > +      if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (arg))) ==
> > 0)
> > +	return arg;
> > +    }
> 
> No { } around single statements, both cases.

Fixed.

> > +  /* Not found?  Create it.
> > +     This allows mimicing of real data but avoids having to
> > specify
> 
> "mimicking".

Fixed.

> > +rtx
> > +function_reader::consolidate_singletons (rtx x)
> > +{
> > +  if (!x)
> > +    return x;
> > +
> > + switch (GET_CODE (x))
> 
> Formatting.

Fixed.

> > +    {
> > +    /* FIXME: do we need to check for VOIDmode for these?  */
> 
> Don't see why we would.

Thanks; removed.

> > +    case CONST_INT:
> > +      return gen_rtx_CONST_INT (GET_MODE (x), INTVAL (x));
> 
> Ugh, really? Can't this be done earlier?

rtx_reader::read_rtx_code reads the name "const_int" from the FILE *
turns it into a RTX_CODE, and allocates a fresh instance via:
   rtx_alloc (code)

I guess we could special-case it at that point, but it would mean
special-casing reading the operands etc also.

By doing it here, we consolidate all of the singleton-consolidation
into one place.

> > +
> > +  add_fixup_source_location (loc, insn, operand_idx,
> > +			     filename, atoi(name.string));
> 
> Space before open paren.

Fixed.

> > +  /* Verify lookup of hard registers.  */
> > +#ifdef GCC_AARCH64_H
> > +  ASSERT_EQ (0, lookup_reg_by_dump_name ("x0"));
> > +  ASSERT_EQ (1, lookup_reg_by_dump_name ("x1"));
> > +#endif
> > +#ifdef I386_OPTS_H
> > +  ASSERT_EQ (0, lookup_reg_by_dump_name ("ax"));
> > +  ASSERT_EQ (1, lookup_reg_by_dump_name ("dx"));
> > +#endif
> 
> Same as before, please don't do this here.

lookup_reg_by_dump_name is static within read-rtl-function.c.  I could
expose it within e.g. rtl.h, and then do the selftests within a target
-specific selftest hook.

Alternatively, I could add the verification of loading hard-reg regnos
to the "loading a dump" selftests I've just split out into separate
patches.

Do you have a preference?

> > +/* Verify that the src and dest indices and flags of edge E are as
> > +   expected, using LOC as the effective location when reporting
> > +   failures.  */
> 
> Again, please document all args (everywhere).

Fixed.

> > +
> > +static void
> > +test_loading_dump_fragment_1 ()
> > +{
> > +  // TODO: filter on target?
> > +  rtl_dump_test t (SELFTEST_LOCATION, locate_file
> > ("asr_div1.rtl"));
> 
> This is in a generic file, isn't it? Yeah, I don't think we want to
> load 
> any RTL from here. Split out such selftests into a separate patch so
> we 
> can figure out what to do with them.

Yes: this is a target-independent dump (I *think*; works with both
x86_64 and aarch64).

I've split out all of them into another patch.

> > +
> > +  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 ^^^
> 
> Why is this a fixme and not just done that way (several instances)?

ASSERT_RTX_PTR_EQ doesn't exist yet; there was some discussion about it
in earlier versions of the patch, but I haven't written it.  It would
be equivalent to ASSERT_EQ, checking pointer equality, but would dump
the mismatching expected vs actual rtx on failure.

Should I convert this to a TODO, or go ahead and implement
ASSERT_RTX_PTR_EQ?

> > +
> > +/* An optional policy class for class function_reader.  */
> > +
> > +struct function_reader_policy
> > +{
> > +  function_reader_policy ()
> > +  {
> > +  }
> > +};
> > +
> > +extern bool read_rtl_function_body (int argc, const char **argv,
> > +				    bool (*parse_opt) (const char
> > *),
> > +				    function_reader_policy
> > *policy);
> 
> The policy seems completely unused, please remove.

Oops, yes; it's a vestige from an older version of pseudo handling.

Removed.

> Also, can't this 
> single declaration go into an existing header?

Probably, but I'm not sure where.

I tried rtl.h, but patch 9 adds read_rtl_function_body_from_file_range.
 This needs to be callable from the C frontend, and rtl.h is poisoned
within frontends.  Hence the new header file.

> > +  /* Handle insn codes in compact dumps.  In such dumps, the code
> > of insns
> > +     is prefixed with "c", giving "cinsn", "cnote" etc, and
> > CODE_LABEL is
> > +     special-cased as "clabel".  */
> > +  if (name[0] == 'c')
> > +    {
> > +      for (i = 0; i < NUM_RTX_CODE; i++)
> > +	if (strcmp (GET_RTX_NAME (i), name + 1) == 0)
> > +	  return i;
> 
> I'd rather check specifically for the ones we expect, to avoid
> surprises.

I've converted it to a table of known values.

> > +#ifdef GENERATOR_FILE
> 
> There's a lot of this. Is there a clean split where stuff could be
> moved 
> into a separate file instead?

Currently we have:

- read-md.c: base .md reading, with base-bones directive-handling
- read-rtl.c: adds the ability to handle hierarchical rtx
- read-rtl-function.c: (new in patch kit): adds the ability to read RTL
function dumps.

So maybe "read-rtl-md.c", for the things that relate to hierarchical
rtx, but which are .md-specific?

Or I could try to move the things to read-md.c, but not sure if that's
going to work.


> > @@ -1103,13 +1233,39 @@ rtx_reader::read_rtx_code (const char
> > *code_name)
> >        rtx value;		/* Value of this node.  */
> >      };
> > 
> > +  one_time_initialization ();
> 
> Isn't there a better place to call this?

Maybe rename to "ensure_initialized"?  This replaces the code:

-  static bool initialized = false;
-
-  /* Do one-time initialization.  */
-  if (!initialized)
-    {
-      initialize_iterators ();
-      initialized = true;
-    }

in rtx_reader::read_rtx, since now not all rtx-reading goes through
that path.

> > +
> > +  /* Use the format_ptr to parse the various operands of this rtx.
> > +     read_rtx_operand is a vfunc, allowing the parser to vary
> > between
> > +     parsing .md files and parsing .rtl dumps; the function_reader
> > subclass
> > +     overrides the defaults when loading RTL dumps, to handle the
> > +     various extra attributes emitted by print_rtx.  */
> 
> Not sure that documentation is really necessary at this point. If 
> someone is looking for the definition of read_rtx_operand they'll
> figure 
> out it's a virtual function anyway.

Given that we rarely use vfuncs, is calling it out more acceptable?
Shall I just drop it, or word it as "Default implementation of..."?

> >    for (int idx = 0; format_ptr[idx] != 0; idx++)
> > -    read_rtx_operand (return_rtx, idx);
> > +    return_rtx = read_rtx_operand (return_rtx, idx);
> > +
> > +  /* Call a vfunc to handle the various additional information
> > that
> > +     print-rtl.c can write after the regular fields; does nothing
> > when
> > +     parsing .md files.  */
> > +  handle_any_trailing_information (return_rtx);
> 
> Same here really.

Maybe reword to:
"Give subclasses a chance to handle any additional information that
may be present after the regular fields (e.g. in a function dump)"
or somesuch.

> > +
> > +	/* "stringbuf" was allocated within string_obstack and
> > thus has
> > +	   the its lifetime restricted to that of the rtx_reader. 
> >  This is
> > +	   OK for the generator programs, but for non-generator
> > programs,
> > +	   XSTR and XTMPL fields are meant to be allocated in the
> > GC-managed
> > +	   heap.  Hence we need to allocate a copy in the GC
> > -managed heap
> > +	   for the non-generator case.  */
> > +	const char *string_ptr = stringbuf;
> > +#ifndef GENERATOR_FILE
> > +	string_ptr = ggc_strdup (stringbuf);
> > +#endif /* #ifndef GENERATOR_FILE */
> 
> Encapsulate in a virtual finalize_string perhaps?

Done.

I'm preparing updated patches (for the issues covered so far...)
Dave
Bernd Schmidt Dec. 2, 2016, 3:28 p.m. UTC | #5
On 12/01/2016 10:43 PM, David Malcolm wrote:
>>> +  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 ^^^
>>
>> Why is this a fixme and not just done that way (several instances)?
>
> ASSERT_RTX_PTR_EQ doesn't exist yet; there was some discussion about it
> in earlier versions of the patch, but I haven't written it.  It would
> be equivalent to ASSERT_EQ, checking pointer equality, but would dump
> the mismatching expected vs actual rtx on failure.
>
> Should I convert this to a TODO, or go ahead and implement
> ASSERT_RTX_PTR_EQ?

Missed this question. Just add ASSERT_RTX_PTR_EQ, that shouldn't be 
hard, should it?


Bernd
diff mbox

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index c265893..73d12dc 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1407,6 +1407,9 @@  OBJS = \
 	print-rtl-function.o \
 	print-tree.o \
 	profile.o \
+	read-md.o \
+	read-rtl.o \
+	read-rtl-function.o \
 	real.o \
 	realmpfr.o \
 	recog.o \
@@ -1434,6 +1437,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/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index b7d4640..e1ab83b 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -64,6 +64,8 @@ 
 #include "sched-int.h"
 #include "target-globals.h"
 #include "common/common-target.h"
+#include "selftest.h"
+#include "selftest-rtl.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
@@ -14168,6 +14170,48 @@  aarch64_optab_supported_p (int op, machine_mode mode1, machine_mode,
     }
 }
 
+/* Target-specific selftests.  */
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that the RTL loader copes a dump from print_rtx_function.
+   This test is target-specific since the dump contains target-specific
+   hard reg names.  */
+
+static void
+aarch64_test_loading_full_dump ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("aarch64/times-two.rtl"));
+
+  ASSERT_STREQ ("times_two", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+
+  rtx_insn *insn_1 = get_insn_by_uid (1);
+  ASSERT_EQ (NOTE, GET_CODE (insn_1));
+
+  rtx_insn *insn_15 = get_insn_by_uid (15);
+  ASSERT_EQ (INSN, GET_CODE (insn_15));
+  ASSERT_EQ (USE, GET_CODE (PATTERN (insn_15)));
+
+  /* Verify crtl->return_rtx.  */
+  ASSERT_EQ (REG, GET_CODE (crtl->return_rtx));
+  ASSERT_EQ (0, REGNO (crtl->return_rtx));
+  ASSERT_EQ (SImode, GET_MODE (crtl->return_rtx));
+}
+
+/* Run all target-specific selftests.  */
+
+static void
+aarch64_run_selftests (void)
+{
+  aarch64_test_loading_full_dump ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
+
 #undef TARGET_ADDRESS_COST
 #define TARGET_ADDRESS_COST aarch64_address_cost
 
@@ -14502,6 +14546,11 @@  aarch64_optab_supported_p (int op, machine_mode mode1, machine_mode,
 #undef TARGET_OMIT_STRUCT_RETURN_REG
 #define TARGET_OMIT_STRUCT_RETURN_REG true
 
+#if CHECKING_P
+#undef TARGET_RUN_TARGET_SELFTESTS
+#define TARGET_RUN_TARGET_SELFTESTS selftest::aarch64_run_selftests
+#endif /* #if CHECKING_P */
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-aarch64.h"
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c
index 6c608e0..0dda786 100644
--- a/gcc/config/i386/i386.c
+++ b/gcc/config/i386/i386.c
@@ -50672,6 +50672,206 @@  ix86_test_dumping_memory_blockage ()
      "            ] UNSPEC_MEMORY_BLOCKAGE)))\n", pat, &r);
 }
 
+/* Verify loading an RTL dump; specifically a dump of copying
+   a param on x86_64 from a hard reg into the frame.
+   This test is target-specific since the dump contains target-specific
+   hard reg names.  */
+
+static void
+ix86_test_loading_dump_fragment_1 ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION,
+		   locate_file ("x86_64/copy-hard-reg-into-frame.rtl"));
+
+  rtx_insn *insn = get_insn_by_uid (1);
+
+  /* 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 the RTL loader copes with a call_insn dump.
+   This test is target-specific since the dump contains a target-specific
+   hard reg name.  */
+
+static void
+ix86_test_loading_call_insn ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("x86_64/call-insn.rtl"));
+
+  rtx_insn *insn = get_insns ();
+  ASSERT_EQ (CALL_INSN, GET_CODE (insn));
+
+  /* "/j".  */
+  ASSERT_TRUE (RTX_FLAG (insn, jump));
+
+  rtx pat = PATTERN (insn);
+  ASSERT_EQ (CALL, GET_CODE (SET_SRC (pat)));
+
+  /* Verify REG_NOTES.  */
+  {
+    /* "(expr_list:REG_CALL_DECL".   */
+    ASSERT_EQ (EXPR_LIST, GET_CODE (REG_NOTES (insn)));
+    rtx_expr_list *note0 = as_a <rtx_expr_list *> (REG_NOTES (insn));
+    ASSERT_EQ (REG_CALL_DECL, REG_NOTE_KIND (note0));
+
+    /* "(expr_list:REG_EH_REGION (const_int 0 [0])".  */
+    rtx_expr_list *note1 = note0->next ();
+    ASSERT_EQ (REG_EH_REGION, REG_NOTE_KIND (note1));
+
+    ASSERT_EQ (NULL, note1->next ());
+  }
+
+  /* Verify CALL_INSN_FUNCTION_USAGE.  */
+  {
+    /* "(expr_list:DF (use (reg:DF 21 xmm0))".  */
+    rtx_expr_list *usage
+      = as_a <rtx_expr_list *> (CALL_INSN_FUNCTION_USAGE (insn));
+    ASSERT_EQ (EXPR_LIST, GET_CODE (usage));
+    ASSERT_EQ (DFmode, GET_MODE (usage));
+    ASSERT_EQ (USE, GET_CODE (usage->element ()));
+    ASSERT_EQ (NULL, usage->next ());
+  }
+}
+
+/* Verify that the RTL loader copes a dump from print_rtx_function.
+   This test is target-specific since the dump contains target-specific
+   hard reg names.  */
+
+static void
+ix86_test_loading_full_dump ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("x86_64/times-two.rtl"));
+
+  ASSERT_STREQ ("times_two", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+
+  rtx_insn *insn_1 = get_insn_by_uid (1);
+  ASSERT_EQ (NOTE, GET_CODE (insn_1));
+
+  rtx_insn *insn_7 = get_insn_by_uid (7);
+  ASSERT_EQ (INSN, GET_CODE (insn_7));
+  ASSERT_EQ (PARALLEL, GET_CODE (PATTERN (insn_7)));
+
+  rtx_insn *insn_15 = get_insn_by_uid (15);
+  ASSERT_EQ (INSN, GET_CODE (insn_15));
+  ASSERT_EQ (USE, GET_CODE (PATTERN (insn_15)));
+
+  /* Verify crtl->return_rtx.  */
+  ASSERT_EQ (REG, GET_CODE (crtl->return_rtx));
+  ASSERT_EQ (0, REGNO (crtl->return_rtx));
+  ASSERT_EQ (SImode, GET_MODE (crtl->return_rtx));
+}
+
+/* Verify that the RTL loader copes with UNSPEC and UNSPEC_VOLATILE insns.
+   In particular, verify that it correctly loads the 2nd operand.
+   This test is target-specific since these are machine-specific
+   operands (and enums).  */
+
+static void
+ix86_test_loading_unspec ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("x86_64/unspec.rtl"));
+
+  ASSERT_STREQ ("test_unspec", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+
+  ASSERT_TRUE (cfun);
+
+  /* Test of an UNSPEC.  */
+   rtx_insn *insn = get_insns ();
+  ASSERT_EQ (INSN, GET_CODE (insn));
+  rtx set = single_set (insn);
+  ASSERT_NE (NULL, set);
+  rtx dst = SET_DEST (set);
+  ASSERT_EQ (MEM, GET_CODE (dst));
+  rtx src = SET_SRC (set);
+  ASSERT_EQ (UNSPEC, GET_CODE (src));
+  ASSERT_EQ (BLKmode, GET_MODE (src));
+  ASSERT_EQ (UNSPEC_MEMORY_BLOCKAGE, XINT (src, 1));
+
+  rtx v0 = XVECEXP (src, 0, 0);
+
+  /* Verify that the two uses of the first SCRATCH have pointer
+     equality.  */
+  rtx scratch_a = XEXP (dst, 0);
+  ASSERT_EQ (SCRATCH, GET_CODE (scratch_a));
+
+  rtx scratch_b = XEXP (v0, 0);
+  ASSERT_EQ (SCRATCH, GET_CODE (scratch_b));
+
+  ASSERT_EQ (scratch_a, scratch_b);
+
+  /* Verify that the two mems are thus treated as equal.  */
+  ASSERT_TRUE (rtx_equal_p (dst, v0));
+
+  /* Verify the the insn is recognized.  */
+  ASSERT_NE(-1, recog_memoized (insn));
+
+  /* Test of an UNSPEC_VOLATILE, which has its own enum values.  */
+  insn = NEXT_INSN (insn);
+  ASSERT_EQ (INSN, GET_CODE (insn));
+
+  set = single_set (insn);
+  ASSERT_NE (NULL, set);
+
+  src = SET_SRC (set);
+  ASSERT_EQ (UNSPEC_VOLATILE, GET_CODE (src));
+  ASSERT_EQ (UNSPECV_RDTSCP, XINT (src, 1));
+}
+
 /* Run all target-specific selftests.  */
 
 static void
@@ -50679,6 +50879,13 @@  ix86_run_selftests (void)
 {
   ix86_test_dumping_hard_regs ();
   ix86_test_dumping_memory_blockage ();
+
+  /* Various tests of loading RTL dumps, here because they contain
+     ix86-isms (e.g. names of hard regs).  */
+  ix86_test_loading_dump_fragment_1 ();
+  ix86_test_loading_call_insn ();
+  ix86_test_loading_full_dump ();
+  ix86_test_loading_unspec ();
 }
 
 } // namespace selftest
diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
index 50cd388..179a91f 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -1371,6 +1371,19 @@  maybe_set_first_label_num (rtx_code_label *x)
   if (CODE_LABEL_NUMBER (x) < first_label_num)
     first_label_num = CODE_LABEL_NUMBER (x);
 }
+
+/* For use by the RTL function loader, when mingling with normal
+   functions.
+   Ensure that label_num is greater than the label num of X, to avoid
+   duplicate labels in the generated assembler.  */
+
+void
+maybe_set_max_label_num (rtx_code_label *x)
+{
+  if (CODE_LABEL_NUMBER (x) >= label_num)
+    label_num = CODE_LABEL_NUMBER (x) + 1;
+}
+
 
 /* Return a value representing some low-order bits of X, where the number
    of low-order bits is given by MODE.  Note that no conversion is done
diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h
index 21c180b..01f16a7 100644
--- a/gcc/emit-rtl.h
+++ b/gcc/emit-rtl.h
@@ -507,4 +507,6 @@  extern int get_mem_align_offset (rtx, unsigned int);
    MODE and adjusted by OFFSET.  */
 extern rtx widen_memory_access (rtx, machine_mode, HOST_WIDE_INT);
 
+extern void maybe_set_max_label_num (rtx_code_label *x);
+
 #endif /* GCC_EMIT_RTL_H */
diff --git a/gcc/function-tests.c b/gcc/function-tests.c
index b0c44cf..90fec6d 100644
--- a/gcc/function-tests.c
+++ b/gcc/function-tests.c
@@ -421,7 +421,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 0b1d168..2674321 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -1901,7 +1901,8 @@  instantiate_decls (tree fndecl)
     instantiate_decl_rtl (DECL_RTL (DECL_VALUE_EXPR (decl)));
 
   /* Now process all variables defined in the function or its subblocks.  */
-  instantiate_decls_1 (DECL_INITIAL (fndecl));
+  if (DECL_INITIAL (fndecl))
+    instantiate_decls_1 (DECL_INITIAL (fndecl));
 
   FOR_EACH_LOCAL_DECL (cfun, ix, decl)
     if (DECL_RTL_SET_P (decl))
diff --git a/gcc/gensupport.c b/gcc/gensupport.c
index c49ad6f..64378e3 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 () : rtx_reader () {}
+  gen_reader () : rtx_reader (false) {}
   void handle_unknown_directive (file_location, const char *);
 };
 
diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c
index 3bbd395..77e6b05 100644
--- a/gcc/print-rtl.c
+++ b/gcc/print-rtl.c
@@ -577,7 +577,7 @@  rtx_writer::print_rtx_operand (const_rtx in_rtx, int idx)
     string:
 
       if (str == 0)
-	fputs (" \"\"", m_outfile);
+	fputs (" (nil)", m_outfile);
       else
 	fprintf (m_outfile, " (\"%s\")", str);
       m_sawclose = 1;
diff --git a/gcc/read-md.c b/gcc/read-md.c
index 6d9a1bd..a8462a6 100644
--- a/gcc/read-md.c
+++ b/gcc/read-md.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
 #include "system.h"
 #include "coretypes.h"
 #include "errors.h"
@@ -424,8 +430,8 @@  md_reader::peek_char (void)
 /* Read an rtx code name into NAME.  It is terminated by any of the
    punctuation chars of rtx printed syntax.  */
 
-void
-md_reader::read_name (struct md_name *name)
+bool
+md_reader::read_name_1 (struct md_name *name, file_location *out_loc)
 {
   int c;
   size_t i;
@@ -463,8 +469,12 @@  md_reader::read_name (struct md_name *name)
       c = read_char ();
     }
 
+  unread_char (c);
+  *out_loc = 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;
@@ -485,6 +495,36 @@  md_reader::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
+md_reader::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
+md_reader::read_name_or_nil (struct md_name *name)
+{
+  file_location loc;
+  if (!read_name_1 (name, &loc))
+    {
+      file_location loc = 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.
@@ -630,6 +670,14 @@  md_reader::read_string (int star_if_braced)
 	obstack_1grow (&m_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);
 
@@ -924,8 +972,9 @@  md_reader::traverse_enum_types (htab_trav callback, void *info)
 
 /* Constructor for md_reader.  */
 
-md_reader::md_reader ()
-: m_toplevel_fname (NULL),
+md_reader::md_reader (bool compact)
+: m_compact (compact),
+  m_toplevel_fname (NULL),
   m_base_dir (NULL),
   m_read_md_file (NULL),
   m_read_md_filename (NULL),
diff --git a/gcc/read-md.h b/gcc/read-md.h
index 8910b75..1be0f5a 100644
--- a/gcc/read-md.h
+++ b/gcc/read-md.h
@@ -94,7 +94,7 @@  struct enum_type {
 class md_reader
 {
  public:
-  md_reader ();
+  md_reader (bool compact);
   virtual ~md_reader ();
 
   bool read_md_files (int, const char **, bool (*) (const char *));
@@ -107,10 +107,13 @@  class md_reader
 
   file_location get_current_location () const;
 
+  bool is_compact () const { return m_compact; }
+
   /* Defined in read-md.c.  */
   int read_char (void);
   void unread_char (int ch);
-  void read_name (struct md_name *name);
+  file_location read_name (struct md_name *name);
+  file_location read_name_or_nil (struct md_name *);
   void read_escape ();
   char *read_quoted_string ();
   char *read_braced_string ();
@@ -167,7 +170,12 @@  class md_reader
   void handle_include (file_location loc);
   void add_include_path (const char *arg);
 
+  bool read_name_1 (struct md_name *name, file_location *out_loc);
+
  private:
+  /* Are we reading a compact dump?  */
+  bool m_compact;
+
   /* The name of the toplevel file that indirectly included
      m_read_md_file.  */
   const char *m_toplevel_fname;
@@ -235,7 +243,7 @@  extern md_reader *md_reader_ptr;
 class noop_reader : public md_reader
 {
  public:
-  noop_reader () : md_reader () {}
+  noop_reader () : md_reader (false) {}
 
   /* A dummy implementation which skips unknown directives.  */
   void handle_unknown_directive (file_location, const char *);
@@ -249,14 +257,25 @@  class noop_reader : public md_reader
 class rtx_reader : public md_reader
 {
  public:
-  rtx_reader ();
+  rtx_reader (bool compact);
   ~rtx_reader ();
 
   bool read_rtx (const char *rtx_name, vec<rtx> *rtxen);
   rtx read_rtx_code (const char *code_name);
-  void read_rtx_operand (rtx return_rtx, int idx);
+  virtual rtx read_rtx_operand (rtx return_rtx, int idx);
   rtx read_nested_rtx ();
   rtx read_rtx_variadic (rtx form);
+  char *read_until (const char *terminator_chars, bool consume_terminator);
+
+  virtual void handle_any_trailing_information (rtx) {}
+  virtual rtx postprocess (rtx x) { return x; }
+
+ protected:
+  /* Analogous to rtx_writer's m_in_call_function_usage.  */
+  bool m_in_call_function_usage;
+
+  /* Support for "reuse_rtx" directives.  */
+  auto_vec<rtx> m_reuse_rtx_by_id;
 };
 
 /* Global singleton; constrast with md_reader_ptr above.  */
diff --git a/gcc/read-rtl-function.c b/gcc/read-rtl-function.c
new file mode 100644
index 0000000..ff6c808
--- /dev/null
+++ b/gcc/read-rtl-function.c
@@ -0,0 +1,2124 @@ 
+/* 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 "memmodel.h"
+#include "emit-rtl.h"
+#include "cgraph.h"
+#include "tree-pass.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"
+#include "insn-config.h"
+#include "recog.h"
+
+/* Forward decls.  */
+class function_reader;
+class fixup;
+
+/* Edges are recorded when parsing the "insn-chain" directive,
+   and created at the end when all the blocks ought to exist.
+   This struct records an "edge-from" or "edge-to" directive seen
+   at LOC, which will be turned into an actual CFG edge once
+   the "insn-chain" is fully parsed.  */
+
+struct deferred_edge
+{
+  deferred_edge (file_location loc, int src_bb_idx, int dest_bb_idx, int flags)
+  : m_loc (loc), m_src_bb_idx (src_bb_idx), m_dest_bb_idx (dest_bb_idx),
+    m_flags (flags)
+  {}
+
+  file_location m_loc;
+  int m_src_bb_idx;
+  int m_dest_bb_idx;
+  int m_flags;
+};
+
+/* Subclass of rtx_reader for reading function dumps.  */
+
+class function_reader : public rtx_reader
+{
+ public:
+  function_reader (function_reader_policy *policy);
+  ~function_reader ();
+
+  /* Overridden vfuncs of class md_reader.  */
+  void handle_unknown_directive (file_location, const char *) FINAL OVERRIDE;
+
+  /* Overridden vfuncs of class rtx_reader.  */
+  rtx read_rtx_operand (rtx return_rtx, int idx) FINAL OVERRIDE;
+  void handle_any_trailing_information (rtx return_rtx) FINAL OVERRIDE;
+  rtx postprocess (rtx) FINAL OVERRIDE;
+
+  rtx_insn **get_insn_by_uid (int uid);
+  tree parse_mem_expr (const char *desc);
+
+ private:
+  void parse_function ();
+  void create_function ();
+  void parse_param ();
+  void parse_insn_chain ();
+  void parse_block ();
+  int parse_bb_idx ();
+  void parse_edge (basic_block block, bool from);
+  rtx_insn *parse_insn (file_location loc, const char *name);
+  void parse_cfg (file_location loc);
+  void parse_crtl (file_location loc);
+  void create_edges ();
+
+  int parse_enum_value (int num_values, const char *const *strings);
+
+  void read_rtx_operand_u (rtx return_rtx, int idx);
+  void read_rtx_operand_i_or_n (rtx return_rtx, int idx, char format_char);
+  rtx read_rtx_operand_r (rtx return_rtx);
+  rtx extra_parsing_for_operand_code_0 (rtx return_rtx, int idx);
+
+  void add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx,
+			   int insn_uid);
+
+  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_expr (file_location loc, rtx x,
+		       const char *desc);
+
+  rtx consolidate_singletons (rtx x);
+  rtx parse_rtx ();
+  void maybe_read_location (int operand_idx, rtx insn);
+
+  void handle_insn_uids ();
+  void apply_fixups ();
+
+ private:
+  function_reader_policy *m_policy;
+  struct uid_hash : int_hash <int, -1, -2> {};
+  hash_map<uid_hash, rtx_insn *> m_insns_by_uid;
+  auto_vec<fixup *> m_fixups;
+  rtx_insn *m_first_insn;
+  auto_vec<tree> m_fake_scope;
+  char *m_name;
+  bool m_have_crtl_directive;
+  basic_block m_bb_to_insert_after;
+  auto_vec <deferred_edge> m_deferred_edges;
+  int m_highest_bb_idx;
+};
+
+/* 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
+   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 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;
+};
+
+/* 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 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 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 ();
+    }
+}
+
+/* Strip trailing whitespace from DESC.  */
+
+static void
+strip_trailing_whitespace (char *desc)
+{
+  char *terminator = desc + strlen (desc);
+  while (desc < terminator)
+    {
+      terminator--;
+      if (ISSPACE (*terminator))
+	*terminator = '\0';
+      else
+	break;
+    }
+}
+
+/* Return the numeric value n for GET_NOTE_INSN_NAME (n) for STRING,
+   or fail if STRING isn't recognized.  */
+
+static int
+parse_note_insn_name (const char *string)
+{
+  for (int i = 0; i < NOTE_INSN_MAX; i++)
+    if (0 == strcmp (string, GET_NOTE_INSN_NAME (i)))
+      return i;
+  fatal_with_file_and_line ("unrecognized NOTE_INSN name: `%s'", string);
+}
+
+/* Return the register number for NAME, or return -1 if it isn't
+   recognized.  */
+
+static int
+lookup_reg_by_dump_name (const char *name)
+{
+  for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (reg_names[i][0]
+	&& ! strcmp (name, reg_names[i]))
+      return i;
+
+  /* Also lookup virtuals.  */
+  if (!strcmp (name, "virtual-incoming-args"))
+    return VIRTUAL_INCOMING_ARGS_REGNUM;
+  if (!strcmp (name, "virtual-stack-vars"))
+    return VIRTUAL_STACK_VARS_REGNUM;
+  if (!strcmp (name, "virtual-stack-dynamic"))
+    return VIRTUAL_STACK_DYNAMIC_REGNUM;
+  if (!strcmp (name, "virtual-outgoing-args"))
+    return VIRTUAL_OUTGOING_ARGS_REGNUM;
+  if (!strcmp (name, "virtual-cfa"))
+    return VIRTUAL_CFA_REGNUM;
+  if (!strcmp (name, "virtual-preferred-stack-boundary"))
+    return VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM;
+  /* TODO: handle "virtual-reg-%d".  */
+
+  /* In compact mode, pseudos are printed with a '%' sigil following
+     by the regno, offset by (LAST_VIRTUAL_REGISTER + 1), so that the
+     first non-virtual pseudo is dumped as "%0".  */
+  if (name[0] == '%')
+    {
+      int dump_num = atoi (name + 1);
+      return dump_num + LAST_VIRTUAL_REGISTER + 1;
+    }
+
+  /* Not found.  */
+  return -1;
+}
+
+/* class function_reader : public rtx_reader */
+
+/* function_reader's constructor.  */
+
+function_reader::function_reader (function_reader_policy *policy)
+: rtx_reader (true),
+  m_policy (policy),
+  m_first_insn (NULL),
+  m_name (NULL),
+  m_have_crtl_directive (false),
+  m_bb_to_insert_after (NULL),
+  m_highest_bb_idx (EXIT_BLOCK)
+{
+}
+
+/* function_reader's destructor.  */
+
+function_reader::~function_reader ()
+{
+  int i;
+  fixup *f;
+  FOR_EACH_VEC_ELT (m_fixups, i, f)
+    delete f;
+
+  free (m_name);
+}
+
+/* Implementation of rtx_reader::handle_unknown_directive.
+
+   Require a top-level "function" elements, as emitted by
+   print_rtx_function, and parse it.  */
+
+void
+function_reader::handle_unknown_directive (file_location start_loc,
+					   const char *name)
+{
+  if (strcmp (name, "function"))
+    fatal_at (start_loc, "expected 'function'");
+
+  parse_function ();
+}
+
+/* Parse the output of print_rtx_function (or hand-written data in the
+   same format), having already parsed the "(function" heading, and
+   finishing immediately before the final ")".
+
+   The "param" and "crtl" clauses are optional.  */
+
+void
+function_reader::parse_function ()
+{
+  m_name = xstrdup (read_string (0));
+
+  create_function ();
+
+  while (1)
+    {
+      int c = read_skip_spaces ();
+      if (c == ')')
+	{
+	  unread_char (c);
+	  break;
+	}
+      unread_char (c);
+      require_char ('(');
+      file_location loc = get_current_location ();
+      struct md_name directive;
+      read_name (&directive);
+      if (strcmp (directive.string, "param") == 0)
+	parse_param ();
+      else if (strcmp (directive.string, "insn-chain") == 0)
+	parse_insn_chain ();
+      else if (strcmp (directive.string, "crtl") == 0)
+	parse_crtl (loc);
+      else
+	fatal_with_file_and_line ("unrecognized directive: %s",
+				  directive.string);
+    }
+
+  handle_insn_uids ();
+
+  apply_fixups ();
+
+  /* Rebuild the JUMP_LABEL field of any JUMP_INSNs in the chain, and the
+     LABEL_NUSES of any CODE_LABELs.
+
+     This has to happen after apply_fixups, since only after then do
+     LABEL_REFs have their label_ref_label set up.  */
+  rebuild_jump_labels (get_insns ());
+
+  crtl->init_stack_alignment ();
+}
+
+/* Set up state for the function *before* fixups are applied.
+
+   Create "cfun" and a decl for the function.
+   By default, every function decl is hardcoded as
+      int test_1 (int i, int j, int k);
+   Set up various other state:
+   - the cfg and basic blocks (edges are created later, *after* fixups
+   are applied).
+   - add the function to the callgraph.  */
+
+void
+function_reader::create_function ()
+{
+  /* Currently we assume cfgrtl mode, rather than cfglayout mode.  */
+  if (0)
+    cfg_layout_rtl_register_cfg_hooks ();
+  else
+    rtl_register_cfg_hooks ();
+
+  /* Create cfun.  */
+  tree fn_name = get_identifier (m_name ? m_name : "test_1");
+  tree int_type = integer_type_node;
+  tree return_type = int_type;
+  tree arg_types[3] = {int_type, int_type, int_type};
+  tree fn_type = build_function_type_array (return_type, 3, arg_types);
+  tree fndecl = build_decl_stat (UNKNOWN_LOCATION, FUNCTION_DECL, fn_name,
+				 fn_type);
+  tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
+			     return_type);
+  DECL_ARTIFICIAL (resdecl) = 1;
+  DECL_IGNORED_P (resdecl) = 1;
+  DECL_RESULT (fndecl) = resdecl;
+  allocate_struct_function (fndecl, false);
+  /* This sets cfun.  */
+
+  current_function_decl = fndecl;
+
+  cfun->curr_properties = (PROP_cfg | PROP_rtl);
+
+  /* Do we need this to force cgraphunit.c to output the function? */
+  DECL_EXTERNAL (fndecl) = 0;
+  DECL_PRESERVE_P (fndecl) = 1;
+
+  /* Add to cgraph.  */
+  {
+    /* cgraph_node::add_new_function does additional processing
+       based on symtab->state.  We need to avoid it attempting to gimplify
+       things.  Temporarily putting it in the PARSING state appears to
+       achieve this.  */
+    enum symtab_state old_state = symtab->state;
+    symtab->state = PARSING;
+    cgraph_node::add_new_function (fndecl, true /*lowered*/);
+    /* Reset the state.  */
+    symtab->state = old_state;
+  }
+
+  /* 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;
+  init_rtl_bb_info (ENTRY_BLOCK_PTR_FOR_FN (cfun));
+  init_rtl_bb_info (EXIT_BLOCK_PTR_FOR_FN (cfun));
+  m_bb_to_insert_after = ENTRY_BLOCK_PTR_FOR_FN (cfun);
+
+}
+
+/* Parse the content of a "param" directive, having already parsed the
+   "(param".  Consume the trailing ')'.  */
+
+void
+function_reader::parse_param ()
+{
+  require_char_ws ('"');
+  char *name = read_quoted_string ();
+
+  /* Lookup param by name.  */
+  tree t_param = parse_mem_expr (name);
+  // TODO: what if not found?
+
+  /* Parse DECL_RTL.  */
+  {
+    require_char_ws ('(');
+    require_word_ws ("DECL_RTL");
+    DECL_WRTL_CHECK (t_param)->decl_with_rtl.rtl = parse_rtx ();
+    require_char_ws (')');
+  }
+
+  /* Parse DECL_RTL_INCOMING.  */
+  {
+    require_char_ws ('(');
+    require_word_ws ("DECL_RTL_INCOMING");
+    DECL_INCOMING_RTL (t_param) = parse_rtx ();
+    require_char_ws (')');
+  }
+
+  require_char_ws (')');
+}
+
+/* Parse zero or more child insn elements within an
+   "insn-chain" element.  Consume the trailing ')'.  */
+
+void
+function_reader::parse_insn_chain ()
+{
+  while (1)
+    {
+      int c = read_skip_spaces ();
+      file_location loc = get_current_location ();
+      if (c == ')')
+	break;
+      else if (c == '(')
+	{
+	  struct md_name directive;
+	  read_name (&directive);
+	  if (strcmp (directive.string, "block") == 0)
+	    parse_block ();
+	  else
+	    parse_insn (loc, directive.string);
+	}
+      else
+	fatal_at (loc, "expected '(' or ')'");
+    }
+
+  create_edges ();
+}
+
+/* Parse zero or more child directives (edges and insns) within a
+   "block" directive, having already parsed the "(block " heading.
+   Consume the trailing ')'.  */
+
+void
+function_reader::parse_block ()
+{
+  /* Parse the index value from the dump.  This will be an integer;
+     we don't support "entry" or "exit" here (unlike for edges).  */
+  struct md_name name;
+  read_name (&name);
+  int bb_idx = atoi (name.string);
+
+  /* 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 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".  */
+
+  if (m_highest_bb_idx < bb_idx)
+    m_highest_bb_idx = bb_idx;
+
+  size_t new_size = m_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);
+
+  last_basic_block_for_fn (cfun) = new_size;
+
+  /* Create the basic block.
+
+     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.  */
+  basic_block bb = alloc_block ();
+  init_rtl_bb_info (bb);
+  bb->index = bb_idx;
+  bb->flags = BB_NEW | BB_RTL;
+  link_block (bb, m_bb_to_insert_after);
+  m_bb_to_insert_after = bb;
+
+  n_basic_blocks_for_fn (cfun)++;
+  SET_BASIC_BLOCK_FOR_FN (cfun, bb_idx, bb);
+  BB_SET_PARTITION (bb, BB_UNPARTITIONED);
+
+  /* Handle insns, edge-from and edge-to directives.  */
+  while (1)
+    {
+      int c = read_skip_spaces ();
+      file_location loc = get_current_location ();
+      if (c == ')')
+	break;
+      else if (c == '(')
+	{
+	  struct md_name directive;
+	  read_name (&directive);
+	  if (strcmp (directive.string, "edge-from") == 0)
+	    parse_edge (bb, true);
+	  else if (strcmp (directive.string, "edge-to") == 0)
+	    parse_edge (bb, false);
+	  else
+	    {
+	      rtx_insn *insn = parse_insn (loc, directive.string);
+	      set_block_for_insn (insn, bb);
+	      if (!BB_HEAD (bb))
+		BB_HEAD (bb) = insn;
+	      BB_END (bb) = insn;
+	    }
+	}
+      else
+	fatal_at (loc, "expected '(' or ')'");
+    }
+}
+
+/* Subroutine of function_reader::parse_edge.
+   Parse a basic block index, handling "entry" and "exit".  */
+
+int
+function_reader::parse_bb_idx ()
+{
+  struct md_name name;
+  read_name (&name);
+  if (strcmp (name.string, "entry") == 0)
+    return ENTRY_BLOCK;
+  if (strcmp (name.string, "exit") == 0)
+    return EXIT_BLOCK;
+  return atoi (name.string);
+}
+
+/* Subroutine of parse_edge_flags.
+   Parse a token such as "FALLTHRU", converting to the flag value.
+   Issue an error if the token is unrecognized.  */
+
+static int
+parse_edge_flag_token (const char *tok)
+{
+#define DEF_EDGE_FLAG(NAME,IDX)		\
+  do {						\
+    if (strcmp (tok, #NAME) == 0)		\
+      return EDGE_##NAME; \
+  } while (0);
+#include "cfg-flags.def"
+#undef DEF_EDGE_FLAG
+  error ("unrecognized edge flag: '%s'", tok);
+  return 0;
+}
+
+/* Subroutine of function_reader::parse_edge.
+   Parse STR and convert to a flag value (or issue an error).
+   The parser uses strtok and hence modifiers STR in-place.  */
+
+static int
+parse_edge_flags (char *str)
+{
+  int result = 0;
+
+  char *tok = strtok (str, "| ");
+  while (tok)
+    {
+      result |= parse_edge_flag_token (tok);
+      tok = strtok (NULL, "| ");
+    }
+
+  return result;
+}
+
+/* Parse an "edge-from" or "edge-to" directive within the "block"
+   directive for BLOCK, having already parsed the "(edge" heading.
+   Consume the final ")".  Record the edge within m_deferred_edges.  */
+
+void
+function_reader::parse_edge (basic_block block, bool from)
+{
+  gcc_assert (block);
+  int this_bb_idx = block->index;
+  file_location loc = get_current_location ();
+  int other_bb_idx = parse_bb_idx ();
+
+  /* "(edge-from 2)" means src = 2, dest = this_bb_idx, whereas
+     "(edge-to 3)" means src = this_bb_idx, dest = 3.  */
+  int src_idx = from ? other_bb_idx : this_bb_idx;
+  int dest_idx = from ? this_bb_idx : other_bb_idx;
+
+  /* Optional "(flags)".  */
+  int flags = 0;
+  int c = read_skip_spaces ();
+  if (c == '(')
+    {
+      require_word_ws ("flags");
+      require_char_ws ('"');
+      char *str = read_quoted_string ();
+      flags = parse_edge_flags (str);
+      require_char_ws (')');
+    }
+  else
+    unread_char (c);
+
+  require_char_ws (')');
+
+  if (0)
+    fprintf (stderr, "parse_edge: %i flags 0x%x \n",
+	     other_bb_idx, flags);
+
+  /* This BB already exists, but the other BB might not yet.
+     For now, save the edges, and create them at the end of insn-chain
+     processing. */
+  /* For now, only process the (edge-from) to this BB, and (edge-to)
+     that go to the exit block; we don't yet verify that the edge-from
+     and edge-to directives are consistent.  */
+  if (from || dest_idx == EXIT_BLOCK)
+    m_deferred_edges.safe_push (deferred_edge (loc, src_idx, dest_idx, flags));
+}
+
+/* Parse rtx instructions by calling read_rtx_code, calling
+   set_first_insn and set_last_insn as appropriate, and
+   adding the insn to the insn chain.
+   Consume the trailing ')'.  */
+
+rtx_insn *
+function_reader::parse_insn (file_location start_loc, const char *name)
+{
+  rtx x = read_rtx_code (name);
+  if (!x)
+    fatal_at (start_loc, "expected insn type; got '%s'", name);
+  rtx_insn *insn = dyn_cast <rtx_insn *> (x);
+  if (!insn)
+    fatal_at (start_loc, "expected insn type; got '%s'", name);
+
+  /* Consume the trailing ')'.  */
+  require_char_ws (')');
+
+  rtx_insn *last_insn = get_last_insn ();
+
+  /* Add "insn" to the insn chain.  */
+  if (last_insn)
+    {
+      gcc_assert (NEXT_INSN (last_insn) == NULL);
+      SET_NEXT_INSN (last_insn) = insn;
+    }
+  SET_PREV_INSN (insn) = last_insn;
+
+  /* Add it to the sequence.  */
+  set_last_insn (insn);
+  if (!m_first_insn)
+    {
+      m_first_insn = insn;
+      set_first_insn (insn);
+    }
+
+  if (rtx_code_label *label = dyn_cast <rtx_code_label *> (insn))
+    maybe_set_max_label_num (label);
+
+  return insn;
+}
+
+/* Postprocessing subroutine for parse_insn_chain: all the basic blocks
+   should have been created by now; create the edges that were seen.  */
+
+void
+function_reader::create_edges ()
+{
+  int i;
+  deferred_edge *de;
+  FOR_EACH_VEC_ELT (m_deferred_edges, i, de)
+    {
+      /* The BBs should already have been created by parse_block.  */
+      basic_block src = BASIC_BLOCK_FOR_FN (cfun, de->m_src_bb_idx);
+      if (!src)
+	fatal_at (de->m_loc, "error: block index %i not found",
+		  de->m_src_bb_idx);
+      basic_block dst = BASIC_BLOCK_FOR_FN (cfun, de->m_dest_bb_idx);
+      if (!dst)
+	fatal_at (de->m_loc, "error: block with index %i not found",
+		  de->m_dest_bb_idx);
+      unchecked_make_edge (src, dst, de->m_flags);
+    }
+}
+
+/* Parse a "crtl" directive, having already parsed the "(crtl" heading.
+   Consume the final ")".  */
+
+void
+function_reader::parse_crtl (file_location loc)
+{
+  if (m_have_crtl_directive)
+    error_at (loc, "more than one 'crtl' directive");
+  m_have_crtl_directive = true;
+
+  /* return_rtx.  */
+  require_char_ws ('(');
+  require_word_ws ("return_rtx");
+  crtl->return_rtx = parse_rtx ();
+  require_char_ws (')');
+
+  require_char_ws (')');
+}
+
+/* Overridden implementation of rtx_reader::read_rtx_operand for
+   function_reader, handling various extra data printed by print_rtx,
+   and sometimes calling the base class implementation.  */
+
+rtx
+function_reader::read_rtx_operand (rtx return_rtx, int idx)
+{
+  RTX_CODE code = GET_CODE (return_rtx);
+  const char *format_ptr = GET_RTX_FORMAT (code);
+  const char format_char = format_ptr[idx];
+  struct md_name name;
+
+  /* Override the regular parser for some format codes.  */
+  switch (format_char)
+    {
+    case 'e':
+      {
+	if (idx == 7 && CALL_P (return_rtx))
+	  {
+	    m_in_call_function_usage = true;
+	    return rtx_reader::read_rtx_operand (return_rtx, idx);
+	    m_in_call_function_usage = false;
+	  }
+	else
+	  return rtx_reader::read_rtx_operand (return_rtx, idx);
+      }
+      break;
+
+    case 'u':
+      read_rtx_operand_u (return_rtx, idx);
+      /* Don't run regular parser for 'u'.  */
+      return return_rtx;
+
+    case 'i':
+    case 'n':
+      read_rtx_operand_i_or_n (return_rtx, idx, format_char);
+      /* Don't run regular parser for these codes.  */
+      return return_rtx;
+
+    case 'B':
+      {
+	gcc_assert (is_compact ());
+	/* Compact mode doesn't store BBs.  */
+	/* Don't run regular parser.  */
+	return return_rtx;
+      }
+      break;
+
+    case 'r':
+      /* Don't run regular parser for 'r'.  */
+      return read_rtx_operand_r (return_rtx);
+
+    default:
+      break;
+    }
+
+  /* Call base class implementation.  */
+  return_rtx = rtx_reader::read_rtx_operand (return_rtx, idx);
+
+  /* Handle any additional parsing needed to handle what the dump
+     could contain.  */
+  switch (format_char)
+    {
+    case '0':
+      return extra_parsing_for_operand_code_0 (return_rtx, idx);
+
+    case 'w':
+      {
+	if (!is_compact ())
+	  {
+	    /* Strip away the redundant hex dump of the value.  */
+	    require_char_ws ('[');
+	    read_name (&name);
+	    require_char_ws (']');
+	  }
+      }
+      break;
+
+    default:
+      break;
+    }
+
+  return return_rtx;
+}
+
+/* Special-cased handling of code 'u' for reading function dumps.
+
+   The RTL file recorded the ID of an insn (or 0 for NULL); we
+   must store this as a pointer, but the insn might not have
+   been loaded yet.  Store the ID away for now, via a fixup.  */
+
+void
+function_reader::read_rtx_operand_u (rtx return_rtx, int idx)
+{
+  /* In compact mode, the PREV/NEXT insn uids are not dumped, so skip
+     the "uu" when reading. */
+  if (is_compact () && GET_CODE (return_rtx) != LABEL_REF)
+    return;
+
+  struct md_name name;
+  file_location loc = read_name (&name);
+  int insn_id = atoi (name.string);
+  if (insn_id)
+    add_fixup_insn_uid (loc, return_rtx, idx, insn_id);
+}
+
+/* Read a name, looking for a match against a string found in array
+   STRINGS of size NUM_VALUES.
+   Return the index of the the matched string, or emit an error.  */
+
+int
+function_reader::parse_enum_value (int num_values, const char *const *strings)
+{
+  struct md_name name;
+  read_name (&name);
+  for (int i = 0; i < num_values; i++)
+    {
+      if (strcmp (name.string, strings[i]) == 0)
+	return i;
+    }
+  error ("unrecognized enum value: '%s'", name.string);
+  return 0;
+}
+
+/* Special-cased handling of codes 'i' and 'n' for reading function
+   dumps.  */
+
+void
+function_reader::read_rtx_operand_i_or_n (rtx return_rtx, int idx,
+					  char format_char)
+{
+  /* Handle some of the extra information that print_rtx
+     can write out for these cases.  */
+  /* print_rtx only writes out operand 5 for notes
+     for NOTE_KIND values NOTE_INSN_DELETED_LABEL
+     and NOTE_INSN_DELETED_DEBUG_LABEL.  */
+  if (idx == 5 && NOTE_P (return_rtx))
+    return;
+
+  if (idx == 4 && INSN_P (return_rtx))
+    {
+      maybe_read_location (idx, return_rtx);
+      return;
+    }
+
+  /* INSN_CODEs aren't printed in compact mode, so don't attempt to
+     parse them.  */
+  if (is_compact ()
+      && INSN_P (return_rtx)
+      && &INSN_CODE (return_rtx) == &XINT (return_rtx, idx))
+    {
+      INSN_CODE (return_rtx) = -1;
+      return;
+    }
+
+  /* Handle UNSPEC and UNSPEC_VOLATILE's operand 1.  */
+#if !defined(GENERATOR_FILE) && NUM_UNSPECV_VALUES > 0
+  if (idx == 1
+      && GET_CODE (return_rtx) == UNSPEC_VOLATILE)
+    {
+      XINT (return_rtx, 1)
+	= parse_enum_value (NUM_UNSPECV_VALUES, unspecv_strings);
+      return;
+    }
+#endif
+#if !defined(GENERATOR_FILE) && NUM_UNSPEC_VALUES > 0
+  if (idx == 1
+      && (GET_CODE (return_rtx) == UNSPEC
+	  || GET_CODE (return_rtx) == UNSPEC_VOLATILE))
+    {
+      XINT (return_rtx, 1)
+	= parse_enum_value (NUM_UNSPEC_VALUES, unspec_strings);
+      return;
+    }
+#endif
+
+  struct md_name name;
+  read_name (&name);
+  int value;
+  if (format_char == 'n')
+    value = parse_note_insn_name (name.string);
+  else
+    value = atoi (name.string);
+  XINT (return_rtx, idx) = value;
+}
+
+/* Special-cased handling of code 'r' for reading function dumps.  */
+
+rtx
+function_reader::read_rtx_operand_r (rtx return_rtx)
+{
+  struct md_name name;
+  file_location loc = read_name (&name);
+  int regno = lookup_reg_by_dump_name (name.string);
+  if (regno == -1)
+    fatal_at (loc, "unrecognized register: '%s'", name.string);
+
+  set_regno_raw (return_rtx, regno, 1);
+
+  /* Consolidate singletons.  */
+  return_rtx = consolidate_singletons (return_rtx);
+
+  ORIGINAL_REGNO (return_rtx) = regno;
+
+  /* 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 = get_current_location ();
+      char *desc = read_until ("]", true);
+      strip_trailing_whitespace (desc);
+      const char *desc_start = desc;
+      /* If ORIGINAL_REGNO (rtx) != regno, we will have:
+	 "orig:%i", ORIGINAL_REGNO (rtx).
+	 Consume it, we don't set ORIGINAL_REGNO, since we can
+	 get that from the 2nd copy later.  */
+      if (0 == strncmp (desc, "orig:", 5))
+	{
+	  expect_original_regno = true;
+	  desc_start += 5;
+	  /* Skip to any whitespace following the integer.  */
+	  const char *space = strchr (desc_start, ' ');
+	  if (space)
+	    desc_start = space + 1;
+	}
+      /* Any remaining text may be the REG_EXPR.  Alternatively we have
+	 no REG_ATTRS, and instead we have ORIGINAL_REGNO.  */
+      if (ISDIGIT (*desc_start))
+	{
+	  /* Assume we have ORIGINAL_REGNO.  */
+	  ORIGINAL_REGNO (return_rtx) = atoi (desc_start);
+	}
+      else
+	{
+	  /* Assume we have REG_EXPR.  */
+	  add_fixup_expr (loc, return_rtx, desc_start);
+	}
+      free (desc);
+    }
+  else
+    unread_char (ch);
+  if (expect_original_regno)
+    {
+      require_char_ws ('[');
+      char *desc = read_until ("]", true);
+      ORIGINAL_REGNO (return_rtx) = atoi (desc);
+      free (desc);
+    }
+
+  return return_rtx;
+}
+
+/* Additional parsing for format code '0' in dumps, handling a variety
+   of special-cases in print_rtx.  */
+
+rtx
+function_reader::extra_parsing_for_operand_code_0 (rtx return_rtx, int idx)
+{
+  RTX_CODE code = GET_CODE (return_rtx);
+  int c;
+  struct md_name name;
+
+  if (idx == 1 && code == SYMBOL_REF)
+    {
+      /* Possibly wrote " [flags %#x]", SYMBOL_REF_FLAGS (in_rtx).  */
+      c = read_skip_spaces ();
+      if (c == '[')
+	{
+	  file_location loc = read_name (&name);
+	  if (strcmp (name.string, "flags"))
+	    error_at (loc, "was expecting `%s'", "flags");
+	  read_name (&name);
+	  SYMBOL_REF_FLAGS (return_rtx) = strtol (name.string, NULL, 16);
+
+	  /* We can't reconstruct SYMBOL_REF_BLOCK; set it to NULL.  */
+	  if (SYMBOL_REF_HAS_BLOCK_INFO_P (return_rtx))
+	    SYMBOL_REF_BLOCK (return_rtx) = NULL;
+
+	  require_char (']');
+	}
+      else
+	unread_char (c);
+
+      /* Possibly wrote:
+	 print_node_brief (outfile, "", SYMBOL_REF_DECL (in_rtx),
+			   dump_flags);  */
+      c = read_skip_spaces ();
+      if (c == '<')
+	{
+	  /* Skip the content for now.  */
+	  while (1)
+	    {
+	      char ch = read_char ();
+	      if (ch == '>')
+		break;
+	    }
+	}
+      else
+	unread_char (c);
+    }
+  else if (idx == 3 && code == NOTE)
+    {
+      /* Note-specific data appears for operand 3, which annoyingly
+	 is before the enum specifying which kind of note we have
+	 (operand 4).  */
+      c = read_skip_spaces ();
+      if (c == '[')
+	{
+	  /* Possibly data for a NOTE_INSN_BASIC_BLOCK, of the form:
+	     [bb %d].  */
+	  file_location bb_loc = read_name (&name);
+	  if (strcmp (name.string, "bb"))
+	    error_at (bb_loc, "was expecting `%s'", "bb");
+	  read_name (&name);
+	  int bb_idx = atoi (name.string);
+	  add_fixup_note_insn_basic_block (bb_loc, return_rtx, idx,
+					   bb_idx);
+	  require_char_ws (']');
+	}
+      else
+	unread_char (c);
+    }
+
+  return return_rtx;
+}
+
+/* Implementation of rtx_reader::handle_any_trailing_information.
+   Handle the various additional information that print-rtl.c can
+   write after the regular fields.  */
+
+void
+function_reader::handle_any_trailing_information (rtx return_rtx)
+{
+  struct md_name name;
+
+  switch (GET_CODE (return_rtx))
+    {
+      case MEM:
+	{
+	  int ch;
+	  require_char_ws ('[');
+	  read_name (&name);
+	  MEM_ALIAS_SET (return_rtx) = atoi (name.string);
+	  /* We have either a MEM_EXPR, or a space.  */
+	  if (peek_char () != ' ')
+	    {
+	      file_location loc = get_current_location ();
+	      char *desc = read_until (" +", false);
+	      add_fixup_expr (loc, consolidate_singletons (return_rtx), desc);
+	      free (desc);
+	    }
+	  else
+	    read_char ();
+
+	  /* We may optionally have '+' for MEM_OFFSET_KNOWN_P.  */
+	  ch = read_skip_spaces ();
+	  if (ch == '+')
+	    {
+	      read_name (&name);
+	      MEM_OFFSET_KNOWN_P (return_rtx) = 1;
+	      MEM_OFFSET (return_rtx) = atoi (name.string);
+	    }
+	  else
+	    unread_char (ch);
+
+	  /* Handle optional " S" for MEM_SIZE.  */
+	  ch = read_skip_spaces ();
+	  if (ch == 'S')
+	    {
+	      read_name (&name);
+	      MEM_SIZE (return_rtx) = atoi (name.string);
+	    }
+	  else
+	    unread_char (ch);
+
+	  /* Handle optional " A" for MEM_ALIGN.  */
+	  ch = read_skip_spaces ();
+	  if (ch == 'A' && peek_char () != 'S')
+	    {
+	      read_name (&name);
+	      MEM_ALIGN (return_rtx) = atoi (name.string);
+	    }
+
+	  /* Handle optional " AS" for MEM_ADDR_SPACE.  */
+	  ch = read_skip_spaces ();
+	  if (ch == 'A' && peek_char () == 'S')
+	    {
+	      read_char ();
+	      read_name (&name);
+	      MEM_ADDR_SPACE (return_rtx) = atoi (name.string);
+	    }
+	  else
+	    unread_char (ch);
+
+	  require_char (']');
+	}
+	break;
+
+      case CODE_LABEL:
+	{
+	  /* Assume that LABEL_NUSES was not dumped.  */
+	  /* TODO: parse LABEL_KIND.  */
+	  /* For now, skip until closing ')'.  */
+	  do
+	    {
+	      char ch = read_char ();
+	      if (ch == ')')
+		{
+		  unread_char (ch);
+		  break;
+		}
+	    }
+	  while (1);
+	}
+	break;
+
+      default:
+	break;
+    }
+}
+
+/* Parse a tree dump for MEM_EXPR and turn it back into a tree.
+   We handle "<retval>", but for anything else we "cheat" by building a
+   global VAR_DECL of type "int" with that name (returning the same global
+   for a name if we see the same name more than once).  */
+
+tree
+function_reader::parse_mem_expr (const char *desc)
+{
+  tree fndecl = cfun->decl;
+
+  if (0 == strcmp (desc, "<retval>"))
+    {
+      return DECL_RESULT (fndecl);
+    }
+
+  /* Search within function parms.  */
+  for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
+    {
+      if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (arg))) == 0)
+	return arg;
+    }
+
+  /* Search within decls we already created.
+     FIXME: use a hash rather than linear search.  */
+  int i;
+  tree t;
+  FOR_EACH_VEC_ELT (m_fake_scope, i, t)
+    if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (t))) == 0)
+      return t;
+
+  /* Not found?  Create it.
+     This allows mimicing of real data but avoids having to specify
+     e.g. names of locals, params etc.
+     Though this way we don't know if we have a PARM_DECL vs a VAR_DECL,
+     and we don't know the types.  Fake it by making everything be
+     a VAR_DECL of "int" type.  */
+  t = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+		  get_identifier (desc),
+		  integer_type_node);
+  m_fake_scope.safe_push (t);
+  return t;
+}
+
+/* Record the information for later post-processing.  */
+
+void
+function_reader::add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx,
+				     int insn_uid)
+{
+  m_fixups.safe_push (new fixup_insn_uid (loc, insn, operand_idx, insn_uid));
+}
+
+/* Record the information for later post-processing.  */
+
+void
+function_reader::add_fixup_note_insn_basic_block (file_location loc, rtx insn,
+						  int operand_idx, int bb_idx)
+{
+  m_fixups.safe_push (new fixup_note_insn_basic_block (loc, insn, operand_idx,
+						       bb_idx));
+}
+
+/* Record the information for later post-processing.  */
+void
+function_reader::add_fixup_source_location (file_location, rtx,
+					    int, const char *, int)
+{
+  /* Empty for now.  */
+}
+
+/* Record the information for later post-processing.  */
+
+void
+function_reader::add_fixup_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));
+}
+
+/* Helper function for consolidate_reg.  */
+
+static rtx
+lookup_global_register (int regno)
+{
+  /* We can't use a switch here, as some of the REGNUMs might not be constants
+     for some targets.  */
+  if (regno == STACK_POINTER_REGNUM)
+      return stack_pointer_rtx;
+  else if (regno ==  FRAME_POINTER_REGNUM)
+    return frame_pointer_rtx;
+  else if (regno == HARD_FRAME_POINTER_REGNUM)
+    return hard_frame_pointer_rtx;
+  else if (regno == ARG_POINTER_REGNUM)
+    return arg_pointer_rtx;
+  else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM)
+    return virtual_incoming_args_rtx;
+  else if (regno == VIRTUAL_STACK_VARS_REGNUM)
+    return virtual_stack_vars_rtx;
+  else if (regno == VIRTUAL_STACK_DYNAMIC_REGNUM)
+    return virtual_stack_dynamic_rtx;
+  else if (regno == VIRTUAL_OUTGOING_ARGS_REGNUM)
+    return virtual_outgoing_args_rtx;
+  else if (regno == VIRTUAL_CFA_REGNUM)
+    return virtual_cfa_rtx;
+  else if (regno == VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM)
+    return virtual_preferred_stack_boundary_rtx;
+#ifdef return_ADDRESS_POINTER_REGNUM
+  else if (regno == RETURN_ADDRESS_POINTER_REGNUM)
+    return return_address_pointer_rtx;
+#endif
+
+  return NULL;
+}
+
+/* Normally REG instances are created by gen_reg_rtx which updates
+   regno_reg_rtx, growing it as necessary.
+   The REG instances created from the dumpfile weren't created this
+   way, so we need to manually update regno_reg_rtx.  */
+
+static void
+ensure_regno (int regno)
+{
+  crtl->emit.ensure_regno_capacity (regno + 1);
+  gcc_assert (regno < crtl->emit.regno_pointer_align_length);
+
+  if (reg_rtx_no < regno + 1)
+    reg_rtx_no = regno + 1;
+}
+
+/* Helper function for consolidate_singletons, for handling REG instances.  */
+
+static rtx
+consolidate_reg (rtx x)
+{
+  gcc_assert (GET_CODE (x) == REG);
+
+  unsigned int regno = REGNO (x);
+
+  ensure_regno (regno);
+
+  /* Some register numbers have their rtx created in init_emit_regs
+     e.g. stack_pointer_rtx for STACK_POINTER_REGNUM.
+     Consolidate on this.  */
+  rtx global_reg = lookup_global_register (regno);
+  if (global_reg)
+    return global_reg;
+
+  /* Populate regno_reg_rtx if necessary.  */
+  if (regno_reg_rtx[regno] == NULL)
+    regno_reg_rtx[regno] = x;
+  /* Use it.  */
+  gcc_assert (GET_CODE (regno_reg_rtx[regno]) == REG);
+  gcc_assert (REGNO (regno_reg_rtx[regno]) == regno);
+  if (GET_MODE (x) == GET_MODE (regno_reg_rtx[regno]))
+    return regno_reg_rtx[regno];
+
+  return x;
+}
+
+/* When reading RTL function dumps, we must consolidate some
+   rtx so that we use singletons where singletons are expected
+   (e.g. we don't want multiple "(const_int 0 [0])" rtx, since
+   these are tested via pointer equality against const0_rtx.  */
+
+rtx
+function_reader::consolidate_singletons (rtx x)
+{
+  if (!x)
+    return x;
+
+ switch (GET_CODE (x))
+    {
+    /* FIXME: do we need to check for VOIDmode for these?  */
+    case PC: return pc_rtx;
+    case RETURN: return ret_rtx;
+    case SIMPLE_RETURN: return simple_return_rtx;
+    case CC0: return cc0_rtx;
+
+    case REG:
+      return consolidate_reg (x);
+
+    case CONST_INT:
+      return gen_rtx_CONST_INT (GET_MODE (x), INTVAL (x));
+
+    default:
+      break;
+    }
+
+  return x;
+}
+
+/* Parse an rtx directive, including both the opening/closing parentheses,
+   and the name.  */
+
+rtx
+function_reader::parse_rtx ()
+{
+  require_char_ws ('(');
+  struct md_name directive;
+  read_name (&directive);
+  rtx result
+    = consolidate_singletons (read_rtx_code (directive.string));
+  require_char_ws (')');
+
+  return result;
+}
+
+/* Implementation of rtx_reader::postprocess for reading function dumps.  */
+
+rtx
+function_reader::postprocess (rtx x)
+{
+  return consolidate_singletons (x);
+}
+
+/* Handle the optional location information written by print_rtx for
+   instructions.  Specifically, operand 4 of instructions (of type "i')
+   is printed thus:
+
+     if (INSN_HAS_LOCATION (in_insn))
+       {
+	 expanded_location xloc = insn_location (in_insn);
+	 fprintf (outfile, " %s:%i", xloc.file, xloc.line);
+       }
+
+    Hence we need to speculatively read a location of the form
+    " %s:%i", and unread the content if there wasn't one.
+
+    Assume that filenames can't contain whitespace, and can't
+    contain ':'.  */
+
+void
+function_reader::maybe_read_location (int operand_idx, rtx insn)
+{
+  file_location loc = get_current_location ();
+
+  /* Skip to first non-whitespace.  */
+  int ch = read_skip_spaces ();
+  auto_vec<char> buf;
+  buf.safe_push (ch);
+  while (1)
+    {
+      int ch = read_char ();
+      /* If we see a ':', assume we have a filename.  */
+      if (ch == ':')
+	{
+	  buf.safe_push ('\0');
+	  break;
+	}
+      buf.safe_push (ch);
+
+      /* If we see a space before ':', assume we don't have a
+	 filename.  */
+      if (ISSPACE (ch))
+	{
+	  while (!buf.is_empty ())
+	    unread_char (buf.pop ());
+	  return;
+	}
+    }
+  char *filename = buf.address ();
+  struct md_name name;
+  read_name (&name);
+
+  add_fixup_source_location (loc, insn, operand_idx,
+			     filename, atoi(name.string));
+}
+
+/* Postprocessing subroutine of function_reader::parse_function.
+   Populate m_insns_by_uid.  */
+
+void
+function_reader::handle_insn_uids ()
+{
+  /* Locate the currently assigned INSN_UID values, storing
+     them in m_insns_by_uid.  */
+  int max_uid = 0;
+  for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      if (m_insns_by_uid.get (INSN_UID (insn)))
+	error ("duplicate insn UID: %i", INSN_UID (insn));
+      m_insns_by_uid.put (INSN_UID (insn), insn);
+      if (INSN_UID (insn) > max_uid)
+	max_uid = INSN_UID (insn);
+    }
+
+  /* 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.  */
+  crtl->emit.x_cur_insn_uid = max_uid + 1;
+}
+
+/* Apply all of the recorded fixups.  */
+
+void
+function_reader::apply_fixups ()
+{
+  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);
+}
+
+/* Run the RTL dump parser.  */
+
+bool
+read_rtl_function_body (int argc, const char **argv,
+			bool (*parse_opt) (const char *),
+			function_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;
+
+  return true;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that parse_edge_flags works.  */
+
+static void
+test_edge_flags ()
+{
+  /* parse_edge_flags modifies its input (due to strtok), so we must make
+     a copy of the literals.  */
+#define ASSERT_PARSE_EDGE_FLAGS(EXPECTED, STR) \
+  do { \
+    char *str = xstrdup (STR); \
+    ASSERT_EQ (EXPECTED, parse_edge_flags (str)); \
+    free (str); \
+  } while (0)
+
+  ASSERT_PARSE_EDGE_FLAGS (0, "");
+  ASSERT_PARSE_EDGE_FLAGS (EDGE_FALLTHRU, "FALLTHRU");
+  ASSERT_PARSE_EDGE_FLAGS (EDGE_ABNORMAL_CALL, "ABNORMAL_CALL");
+  ASSERT_PARSE_EDGE_FLAGS (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL,
+			   "ABNORMAL | ABNORMAL_CALL");
+
+#undef  ASSERT_PARSE_EDGE_FLAGS
+}
+
+/* Verify that lookup_reg_by_dump_name works.  */
+
+static void
+test_parsing_regnos ()
+{
+  ASSERT_EQ (-1, lookup_reg_by_dump_name ("this is not a register"));
+
+  /* Verify lookup of hard registers.  */
+#ifdef GCC_AARCH64_H
+  ASSERT_EQ (0, lookup_reg_by_dump_name ("x0"));
+  ASSERT_EQ (1, lookup_reg_by_dump_name ("x1"));
+#endif
+#ifdef I386_OPTS_H
+  ASSERT_EQ (0, lookup_reg_by_dump_name ("ax"));
+  ASSERT_EQ (1, lookup_reg_by_dump_name ("dx"));
+#endif
+
+  /* Verify lookup of virtual registers.  */
+  ASSERT_EQ (VIRTUAL_INCOMING_ARGS_REGNUM,
+    lookup_reg_by_dump_name ("virtual-incoming-args"));
+  ASSERT_EQ (VIRTUAL_STACK_VARS_REGNUM,
+    lookup_reg_by_dump_name ("virtual-stack-vars"));
+  ASSERT_EQ (VIRTUAL_STACK_DYNAMIC_REGNUM,
+    lookup_reg_by_dump_name ("virtual-stack-dynamic"));
+  ASSERT_EQ (VIRTUAL_OUTGOING_ARGS_REGNUM,
+    lookup_reg_by_dump_name ("virtual-outgoing-args"));
+  ASSERT_EQ (VIRTUAL_CFA_REGNUM,
+    lookup_reg_by_dump_name ("virtual-cfa"));
+  ASSERT_EQ (VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM,
+    lookup_reg_by_dump_name ("virtual-preferred-stack-boundary"));
+
+  /* Verify lookup of non-virtual pseudos.  */
+  ASSERT_EQ (LAST_VIRTUAL_REGISTER + 1, lookup_reg_by_dump_name ("%0"));
+  ASSERT_EQ (LAST_VIRTUAL_REGISTER + 2, lookup_reg_by_dump_name ("%1"));
+}
+
+/* Verify that the src and dest indices and flags of edge E are as
+   expected, using LOC as the effective location when reporting
+   failures.  */
+
+static void
+assert_edge_at (const location &loc, edge e, int expected_src_idx,
+		int expected_dest_idx, int expected_flags)
+{
+  ASSERT_EQ_AT (loc, expected_src_idx, e->src->index);
+  ASSERT_EQ_AT (loc, expected_dest_idx, e->dest->index);
+  ASSERT_EQ_AT (loc, expected_flags, e->flags);
+}
+
+/* Verify that the src and dest indices and flags of EDGE are as
+   expected.  */
+
+#define ASSERT_EDGE(EDGE, EXPECTED_SRC_IDX, EXPECTED_DEST_IDX,		\
+		    EXPECTED_FLAGS)					\
+  assert_edge_at (SELFTEST_LOCATION, EDGE, EXPECTED_SRC_IDX, \
+		  EXPECTED_DEST_IDX, EXPECTED_FLAGS)
+
+/* Verify that we can load RTL dumps.  */
+
+static void
+test_loading_dump_fragment_1 ()
+{
+  // TODO: filter on target?
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("asr_div1.rtl"));
+
+  /* Verify that the insns were loaded correctly.  */
+  rtx_insn *insn_1 = get_insns ();
+  ASSERT_TRUE (insn_1);
+  ASSERT_EQ (1, INSN_UID (insn_1));
+  ASSERT_EQ (INSN, GET_CODE (insn_1));
+  ASSERT_EQ (SET, GET_CODE (PATTERN (insn_1)));
+  ASSERT_EQ (NULL, PREV_INSN (insn_1));
+
+  rtx_insn *insn_2 = NEXT_INSN (insn_1);
+  ASSERT_TRUE (insn_2);
+  ASSERT_EQ (2, INSN_UID (insn_2));
+  ASSERT_EQ (INSN, GET_CODE (insn_2));
+  ASSERT_EQ (insn_1, PREV_INSN (insn_2));
+  ASSERT_EQ (NULL, NEXT_INSN (insn_2));
+
+  /* Verify that registers were loaded correctly.  */
+  rtx insn_1_dest = SET_DEST (PATTERN (insn_1));
+  ASSERT_EQ (REG, GET_CODE (insn_1_dest));
+  ASSERT_EQ ((LAST_VIRTUAL_REGISTER + 1) + 2, REGNO (insn_1_dest));
+  rtx insn_1_src = SET_SRC (PATTERN (insn_1));
+  ASSERT_EQ (LSHIFTRT, GET_CODE (insn_1_src));
+  rtx reg = XEXP (insn_1_src, 0);
+  ASSERT_EQ (REG, GET_CODE (reg));
+  ASSERT_EQ (LAST_VIRTUAL_REGISTER + 1, REGNO (reg));
+
+  /* Verify that get_insn_by_uid works.  */
+  ASSERT_EQ (insn_1, get_insn_by_uid (1));
+  ASSERT_EQ (insn_2, get_insn_by_uid (2));
+
+  /* Verify that basic blocks were created.  */
+  ASSERT_EQ (2, BLOCK_FOR_INSN (insn_1)->index);
+  ASSERT_EQ (2, BLOCK_FOR_INSN (insn_2)->index);
+
+  /* Verify that the CFG was recreated.  */
+  ASSERT_TRUE (cfun);
+  verify_three_block_rtl_cfg (cfun);
+  basic_block bb2 = BASIC_BLOCK_FOR_FN (cfun, 2);
+  ASSERT_TRUE (bb2 != NULL);
+  ASSERT_EQ (BB_RTL, bb2->flags & BB_RTL);
+  ASSERT_EQ (2, bb2->index);
+  ASSERT_EQ (insn_1, BB_HEAD (bb2));
+  ASSERT_EQ (insn_2, BB_END (bb2));
+}
+
+/* Verify loading another RTL dump.  */
+
+static void
+test_loading_dump_fragment_2 ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("simple-cse.rtl"));
+
+  rtx_insn *insn_1 = get_insn_by_uid (1);
+  rtx_insn *insn_2 = get_insn_by_uid (2);
+  rtx_insn *insn_3 = get_insn_by_uid (3);
+
+  rtx set1 = single_set (insn_1);
+  ASSERT_NE (NULL, set1);
+  rtx set2 = single_set (insn_2);
+  ASSERT_NE (NULL, set2);
+  rtx set3 = single_set (insn_3);
+  ASSERT_NE (NULL, set3);
+
+  rtx src1 = SET_SRC (set1);
+  ASSERT_EQ (PLUS, GET_CODE (src1));
+
+  rtx src2 = SET_SRC (set2);
+  ASSERT_EQ (PLUS, GET_CODE (src2));
+
+  /* Both src1 and src2 refer to "(reg:SI %0)".
+     Verify that we have pointer equality.  */
+  rtx lhs1 = XEXP (src1, 0);
+  rtx lhs2 = XEXP (src2, 0);
+  ASSERT_EQ (lhs1, lhs2);
+
+  /* Verify that the CFG was recreated. */
+  ASSERT_TRUE (cfun);
+  verify_three_block_rtl_cfg (cfun);
+}
+
+/* Verify that CODE_LABEL insns are loaded correctly.  */
+
+static void
+test_loading_labels ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("example-labels.rtl"));
+
+  rtx_insn *insn_100 = get_insn_by_uid (100);
+  ASSERT_EQ (CODE_LABEL, GET_CODE (insn_100));
+  ASSERT_EQ (100, INSN_UID (insn_100));
+  ASSERT_EQ (NULL, LABEL_NAME (insn_100));
+  ASSERT_EQ (0, LABEL_NUSES (insn_100));
+  ASSERT_EQ (30, CODE_LABEL_NUMBER (insn_100));
+
+  rtx_insn *insn_200 = get_insn_by_uid (200);
+  ASSERT_EQ (CODE_LABEL, GET_CODE (insn_200));
+  ASSERT_EQ (200, INSN_UID (insn_200));
+  ASSERT_STREQ ("some_label_name", LABEL_NAME (insn_200));
+  ASSERT_EQ (0, LABEL_NUSES (insn_200));
+  ASSERT_EQ (40, CODE_LABEL_NUMBER (insn_200));
+
+  /* Ensure that the presence of CODE_LABEL_NUMBER == 40
+     means that the next label num to be handed out will be 41.  */
+  ASSERT_EQ (41, max_label_num ());
+
+  /* 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_200));
+  ASSERT_TRUE (ggc_marked_p (LABEL_NAME (insn_200)));
+}
+
+/* Verify that the loader copes with an insn with a mode.  */
+
+static void
+test_loading_insn_with_mode ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("insn-with-mode.rtl"));
+  rtx_insn *insn = get_insns ();
+  ASSERT_EQ (INSN, GET_CODE (insn));
+
+  /* Verify that the "TI" mode was set from "insn:TI".  */
+  ASSERT_EQ (TImode, GET_MODE (insn));
+}
+
+/* Verify that the loader copes with a jump_insn to a label_ref.  */
+
+static void
+test_loading_jump_to_label_ref ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("jump-to-label-ref.rtl"));
+
+  rtx_insn *jump_insn = get_insn_by_uid (1);
+  ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
+
+  rtx_insn *barrier = get_insn_by_uid (2);
+  ASSERT_EQ (BARRIER, GET_CODE (barrier));
+
+  rtx_insn *code_label = get_insn_by_uid (100);
+  ASSERT_EQ (CODE_LABEL, GET_CODE (code_label));
+
+  /* Verify the jump_insn. */
+  ASSERT_EQ (4, BLOCK_FOR_INSN (jump_insn)->index);
+  ASSERT_EQ (SET, GET_CODE (PATTERN (jump_insn)));
+  /* Ensure that the "(pc)" is using the global singleton.  */
+  ASSERT_EQ (pc_rtx, SET_DEST (PATTERN (jump_insn)));
+  // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^
+  rtx label_ref = SET_SRC (PATTERN (jump_insn));
+  ASSERT_EQ (LABEL_REF, GET_CODE (label_ref));
+  ASSERT_EQ (code_label, label_ref_label (label_ref));
+  ASSERT_EQ (code_label, JUMP_LABEL (jump_insn));
+
+  /* Verify the code_label. */
+  ASSERT_EQ (5, BLOCK_FOR_INSN (code_label)->index);
+  ASSERT_EQ (NULL, LABEL_NAME (code_label));
+  ASSERT_EQ (1, LABEL_NUSES (code_label));
+
+  /* Verify the generated CFG.  */
+
+  /* Locate blocks.  */
+  basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun);
+  ASSERT_TRUE (entry != NULL);
+  ASSERT_EQ (ENTRY_BLOCK, entry->index);
+
+  basic_block exit = EXIT_BLOCK_PTR_FOR_FN (cfun);
+  ASSERT_TRUE (exit != NULL);
+  ASSERT_EQ (EXIT_BLOCK, exit->index);
+
+  basic_block bb4 = (*cfun->cfg->x_basic_block_info)[4];
+  basic_block bb5 = (*cfun->cfg->x_basic_block_info)[5];
+  ASSERT_EQ (4, bb4->index);
+  ASSERT_EQ (5, bb5->index);
+
+  /* Entry block.  */
+  ASSERT_EQ (NULL, entry->preds);
+  ASSERT_EQ (1, entry->succs->length ());
+  ASSERT_EDGE ((*entry->succs)[0], 0, 4, EDGE_FALLTHRU);
+
+  /* bb4.  */
+  ASSERT_EQ (1, bb4->preds->length ());
+  ASSERT_EDGE ((*bb4->preds)[0], 0, 4, EDGE_FALLTHRU);
+  ASSERT_EQ (1, bb4->succs->length ());
+  ASSERT_EDGE ((*bb4->succs)[0], 4, 5, 0x0);
+
+  /* bb5.  */
+  ASSERT_EQ (1, bb5->preds->length ());
+  ASSERT_EDGE ((*bb5->preds)[0], 4, 5, 0x0);
+  ASSERT_EQ (1, bb5->succs->length ());
+  ASSERT_EDGE ((*bb5->succs)[0], 5, 1, EDGE_FALLTHRU);
+
+  /* Exit block.  */
+  ASSERT_EQ (1, exit->preds->length ());
+  ASSERT_EDGE ((*exit->preds)[0], 5, 1, EDGE_FALLTHRU);
+  ASSERT_EQ (NULL, exit->succs);
+}
+
+/* Verify that the loader copes with a jump_insn to a label_ref
+   marked "return".  */
+
+static void
+test_loading_jump_to_return ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("jump-to-return.rtl"));
+
+  rtx_insn *jump_insn = get_insn_by_uid (1);
+  ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
+  ASSERT_EQ (ret_rtx, JUMP_LABEL (jump_insn));
+  // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^
+}
+
+/* Verify that the loader copes with a jump_insn to a label_ref
+   marked "simple_return".  */
+
+static void
+test_loading_jump_to_simple_return ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION,
+		   locate_file ("jump-to-simple-return.rtl"));
+
+  rtx_insn *jump_insn = get_insn_by_uid (1);
+  ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
+  ASSERT_EQ (simple_return_rtx, JUMP_LABEL (jump_insn));
+  // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^
+}
+
+/* Verify that the loader copes with a NOTE_INSN_BASIC_BLOCK.  */
+
+static void
+test_loading_note_insn_basic_block ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION,
+		   locate_file ("note_insn_basic_block.rtl"));
+
+  rtx_insn *note = get_insn_by_uid (1);
+  ASSERT_EQ (NOTE, GET_CODE (note));
+  ASSERT_EQ (2, BLOCK_FOR_INSN (note)->index);
+
+  ASSERT_EQ (NOTE_INSN_BASIC_BLOCK, NOTE_KIND (note));
+  ASSERT_EQ (2, NOTE_BASIC_BLOCK (note)->index);
+  ASSERT_EQ (BASIC_BLOCK_FOR_FN (cfun, 2), NOTE_BASIC_BLOCK (note));
+}
+
+/* Verify that the loader copes with a NOTE_INSN_DELETED.  */
+
+static void
+test_loading_note_insn_deleted ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("note-insn-deleted.rtl"));
+
+  rtx_insn *note = get_insn_by_uid (1);
+  ASSERT_EQ (NOTE, GET_CODE (note));
+  ASSERT_EQ (NOTE_INSN_DELETED, NOTE_KIND (note));
+}
+
+/* Verify that the const_int values are consolidated, since
+   pointer equality corresponds to value equality.
+   TODO: do this for all in CASE_CONST_UNIQUE.  */
+
+static void
+test_loading_const_int ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("const-int.rtl"));
+
+  /* Verify that const_int values below MAX_SAVED_CONST_INT use
+     the global values.  */
+  ASSERT_EQ (const0_rtx, SET_SRC (PATTERN (get_insn_by_uid (1))));
+  ASSERT_EQ (const1_rtx, SET_SRC (PATTERN (get_insn_by_uid (2))));
+  ASSERT_EQ (constm1_rtx, SET_SRC (PATTERN (get_insn_by_uid (3))));
+
+  /* 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 (4))));
+}
+
+/* Verify that the loader copes with a SYMBOL_REF.  */
+
+static void
+test_loading_symbol_ref ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("symbol-ref.rtl"));
+
+  rtx_insn *insn = get_insns ();
+
+  rtx high = SET_SRC (PATTERN (insn));
+  ASSERT_EQ (HIGH, GET_CODE (high));
+
+  rtx symbol_ref = XEXP (high, 0);
+  ASSERT_EQ (SYMBOL_REF, GET_CODE (symbol_ref));
+
+  /* Verify that "[flags 0xc0]" was parsed.  */
+  ASSERT_EQ (0xc0, SYMBOL_REF_FLAGS (symbol_ref));
+  /* TODO: we don't yet load SYMBOL_REF_DECL.  */
+}
+
+/* Verify that the loader can rebuild a CFG.  */
+
+static void
+test_loading_cfg ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("cfg-test.rtl"));
+
+  ASSERT_STREQ ("cfg_test", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+
+  ASSERT_TRUE (cfun);
+
+  ASSERT_TRUE (cfun->cfg != NULL);
+  ASSERT_EQ (6, n_basic_blocks_for_fn (cfun));
+  ASSERT_EQ (6, n_edges_for_fn (cfun));
+
+  /* The "fake" basic blocks.  */
+  basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun);
+  ASSERT_TRUE (entry != NULL);
+  ASSERT_EQ (ENTRY_BLOCK, entry->index);
+
+  basic_block exit = EXIT_BLOCK_PTR_FOR_FN (cfun);
+  ASSERT_TRUE (exit != NULL);
+  ASSERT_EQ (EXIT_BLOCK, exit->index);
+
+  /* The "real" basic blocks.  */
+  basic_block bb2 = (*cfun->cfg->x_basic_block_info)[2];
+  basic_block bb3 = (*cfun->cfg->x_basic_block_info)[3];
+  basic_block bb4 = (*cfun->cfg->x_basic_block_info)[4];
+  basic_block bb5 = (*cfun->cfg->x_basic_block_info)[5];
+
+  ASSERT_EQ (2, bb2->index);
+  ASSERT_EQ (3, bb3->index);
+  ASSERT_EQ (4, bb4->index);
+  ASSERT_EQ (5, bb5->index);
+
+  /* Verify connectivity.  */
+
+  /* Entry block.  */
+  ASSERT_EQ (NULL, entry->preds);
+  ASSERT_EQ (1, entry->succs->length ());
+  ASSERT_EDGE ((*entry->succs)[0], 0, 2, EDGE_FALLTHRU);
+
+  /* bb2.  */
+  ASSERT_EQ (1, bb2->preds->length ());
+  ASSERT_EDGE ((*bb2->preds)[0], 0, 2, EDGE_FALLTHRU);
+  ASSERT_EQ (2, bb2->succs->length ());
+  ASSERT_EDGE ((*bb2->succs)[0], 2, 3, EDGE_TRUE_VALUE);
+  ASSERT_EDGE ((*bb2->succs)[1], 2, 4, EDGE_FALSE_VALUE);
+
+  /* bb3.  */
+  ASSERT_EQ (1, bb3->preds->length ());
+  ASSERT_EDGE ((*bb3->preds)[0], 2, 3, EDGE_TRUE_VALUE);
+  ASSERT_EQ (1, bb3->succs->length ());
+  ASSERT_EDGE ((*bb3->succs)[0], 3, 5, EDGE_FALLTHRU);
+
+  /* bb4.  */
+  ASSERT_EQ (1, bb4->preds->length ());
+  ASSERT_EDGE ((*bb4->preds)[0], 2, 4, EDGE_FALSE_VALUE);
+  ASSERT_EQ (1, bb4->succs->length ());
+  ASSERT_EDGE ((*bb4->succs)[0], 4, 5, EDGE_FALLTHRU);
+
+  /* bb5.  */
+  ASSERT_EQ (2, bb5->preds->length ());
+  ASSERT_EDGE ((*bb5->preds)[0], 3, 5, EDGE_FALLTHRU);
+  ASSERT_EDGE ((*bb5->preds)[1], 4, 5, EDGE_FALLTHRU);
+  ASSERT_EQ (1, bb5->succs->length ());
+  ASSERT_EDGE ((*bb5->succs)[0], 5, 1, EDGE_FALLTHRU);
+
+  /* Exit block.  */
+  ASSERT_EQ (1, exit->preds->length ());
+  ASSERT_EDGE ((*exit->preds)[0], 5, 1, EDGE_FALLTHRU);
+  ASSERT_EQ (NULL, exit->succs);
+}
+
+/* Verify that the loader copes with sparse block indices.
+   This testcase loads a file with a "(block 42)".  */
+
+static void
+test_loading_bb_index ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("bb-index.rtl"));
+
+  ASSERT_STREQ ("test_bb_index", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+
+  ASSERT_TRUE (cfun);
+
+  ASSERT_TRUE (cfun->cfg != NULL);
+  ASSERT_EQ (3, n_basic_blocks_for_fn (cfun));
+  ASSERT_EQ (43, basic_block_info_for_fn (cfun)->length ());
+  ASSERT_EQ (2, n_edges_for_fn (cfun));
+
+  ASSERT_EQ (NULL, (*cfun->cfg->x_basic_block_info)[41]);
+  basic_block bb42 = (*cfun->cfg->x_basic_block_info)[42];
+  ASSERT_NE (NULL, bb42);
+  ASSERT_EQ (42, bb42->index);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+read_rtl_function_c_tests ()
+{
+  test_edge_flags ();
+  test_parsing_regnos ();
+  test_loading_dump_fragment_1 ();
+  test_loading_dump_fragment_2 ();
+  test_loading_labels ();
+  test_loading_insn_with_mode ();
+  test_loading_jump_to_label_ref ();
+  test_loading_jump_to_return ();
+  test_loading_jump_to_simple_return ();
+  test_loading_note_insn_basic_block ();
+  test_loading_note_insn_deleted ();
+  test_loading_const_int ();
+  test_loading_symbol_ref ();
+  test_loading_cfg ();
+  test_loading_bb_index ();
+}
+
+} // 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..036fcce
--- /dev/null
+++ b/gcc/read-rtl-function.h
@@ -0,0 +1,36 @@ 
+/* read-rtl-function.h - Reader for RTL function dumps
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_READ_RTL_FUNCTION_H
+#define GCC_READ_RTL_FUNCTION_H
+
+/* An optional policy class for class function_reader.  */
+
+struct function_reader_policy
+{
+  function_reader_policy ()
+  {
+  }
+};
+
+extern bool read_rtl_function_body (int argc, const char **argv,
+				    bool (*parse_opt) (const char *),
+				    function_reader_policy *policy);
+
+#endif /* GCC_READ_RTL_FUNCTION_H */
diff --git a/gcc/read-rtl.c b/gcc/read-rtl.c
index f74c875..8ef6eec 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,12 @@  along with GCC; see the file COPYING3.  If not see
 #include "read-md.h"
 #include "gensupport.h"
 
+#ifndef GENERATOR_FILE
+#include "function.h"
+#include "memmodel.h"
+#include "emit-rtl.h"
+#endif
+
 /* One element in a singly-linked list of (integer, string) pairs.  */
 struct map_value {
   struct map_value *next;
@@ -106,6 +118,7 @@  htab_t subst_attr_to_iter_map = NULL;
 const char *current_iterator_name;
 
 static void validate_const_int (const char *);
+static void one_time_initialization (void);
 
 /* Global singleton.  */
 rtx_reader *rtx_reader_ptr = NULL;
@@ -153,6 +166,18 @@  find_code (const char *name)
     if (strcmp (GET_RTX_NAME (i), name) == 0)
       return i;
 
+  /* Handle insn codes in compact dumps.  In such dumps, the code of insns
+     is prefixed with "c", giving "cinsn", "cnote" etc, and CODE_LABEL is
+     special-cased as "clabel".  */
+  if (name[0] == 'c')
+    {
+      for (i = 0; i < NUM_RTX_CODE; i++)
+	if (strcmp (GET_RTX_NAME (i), name + 1) == 0)
+	  return i;
+      if (strcmp ("clabel", name) == 0)
+	return CODE_LABEL;
+    }
+
   fatal_with_file_and_line ("unknown rtx code `%s'", name);
 }
 
@@ -181,6 +206,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 +279,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 +447,8 @@  md_reader::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 +612,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 +687,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 +758,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 +801,7 @@  md_reader::read_conditions ()
       add_c_test (expr, value);
     }
 }
+#endif /* #ifdef GENERATOR_FILE */
 
 static void
 validate_const_int (const char *string)
@@ -861,6 +898,8 @@  md_reader::record_potential_iterator_use (struct iterator_group *group,
     }
 }
 
+#ifdef GENERATOR_FILE
+
 /* Finish reading a declaration of the form:
 
        (define... <name> [<value1> ... <valuen>])
@@ -1020,14 +1059,7 @@  check_code_iterator (struct mapping *iterator)
 bool
 rtx_reader::read_rtx (const char *rtx_name, vec<rtx> *rtxen)
 {
-  static bool initialized = false;
-
-  /* Do one-time initialization.  */
-  if (!initialized)
-    {
-      initialize_iterators ();
-      initialized = true;
-    }
+  one_time_initialization ();
 
   /* Handle various rtx-related declarations that aren't themselves
      encoded as rtxes.  */
@@ -1082,6 +1114,103 @@  rtx_reader::read_rtx (const char *rtx_name, vec<rtx> *rtxen)
   return true;
 }
 
+#endif /* #ifdef GENERATOR_FILE */
+
+/* Do one-time initialization.  */
+
+static void
+one_time_initialization (void)
+{
+  static bool initialized = false;
+
+  if (!initialized)
+    {
+      initialize_iterators ();
+      initialized = true;
+    }
+}
+
+/* Consume characters until encountering a character in TERMINATOR_CHARS,
+   consuming the terminator character if CONSUME_TERMINATOR is true.
+   Return all characters before the terminator as an allocated buffer.  */
+
+char *
+rtx_reader::read_until (const char *terminator_chars, bool consume_terminator)
+{
+  int ch = read_skip_spaces ();
+  unread_char (ch);
+  auto_vec<char> buf;
+  while (1)
+    {
+      ch = read_char ();
+      if (strchr (terminator_chars, ch))
+	{
+	  if (!consume_terminator)
+	    unread_char (ch);
+	  break;
+	}
+      buf.safe_push (ch);
+    }
+  buf.safe_push ('\0');
+  return xstrdup (buf.address ());
+}
+
+/* Subroutine of read_rtx_code, for parsing zero or more flags.  */
+
+static void
+read_flags (rtx return_rtx)
+{
+  while (1)
+    {
+      int ch = read_char ();
+      if (ch != '/')
+	{
+	  unread_char (ch);
+	  break;
+	}
+
+      int flag_char = read_char ();
+      switch (flag_char)
+	{
+	  case 's':
+	    RTX_FLAG (return_rtx, in_struct) = 1;
+	    break;
+	  case 'v':
+	    RTX_FLAG (return_rtx, volatil) = 1;
+	    break;
+	  case 'u':
+	    RTX_FLAG (return_rtx, unchanging) = 1;
+	    break;
+	  case 'f':
+	    RTX_FLAG (return_rtx, frame_related) = 1;
+	    break;
+	  case 'j':
+	    RTX_FLAG (return_rtx, jump) = 1;
+	    break;
+	  case 'c':
+	    RTX_FLAG (return_rtx, call) = 1;
+	    break;
+	  case 'i':
+	    RTX_FLAG (return_rtx, return_val) = 1;
+	    break;
+	  default:
+	    fatal_with_file_and_line ("unrecognized flag: `%c'", flag_char);
+	}
+    }
+}
+
+/* Return the numeric value n for GET_REG_NOTE_NAME (n) for STRING,
+   or fail if STRING isn't recognized.  */
+
+static int
+parse_reg_note_name (const char *string)
+{
+  for (int i = 0; i < REG_NOTE_MAX; i++)
+    if (0 == strcmp (string, GET_REG_NOTE_NAME (i)))
+      return i;
+  fatal_with_file_and_line ("unrecognized REG_NOTE name: `%s'", string);
+}
+
 /* Subroutine of read_rtx and read_nested_rtx.  CODE_NAME is the name of
    either an rtx code or a code iterator.  Parse the rest of the rtx and
    return it.  */
@@ -1090,11 +1219,12 @@  rtx
 rtx_reader::read_rtx_code (const char *code_name)
 {
   RTX_CODE code;
-  struct mapping *iterator;
+  struct mapping *iterator = NULL;
   const char *format_ptr;
   struct md_name name;
   rtx return_rtx;
   int c;
+  long reuse_id = -1;
 
   /* Linked list structure for making RTXs: */
   struct rtx_list
@@ -1103,13 +1233,39 @@  rtx_reader::read_rtx_code (const char *code_name)
       rtx value;		/* Value of this node.  */
     };
 
+  one_time_initialization ();
+
+  /* Handle reuse_rtx ids e.g. "(0|scratch:DI)".  */
+  if (ISDIGIT (code_name[0]))
+    {
+      reuse_id = atoi (code_name);
+      while (char ch = *code_name++)
+	if (ch == '|')
+	  break;
+    }
+
+  /* Handle "reuse_rtx".  */
+  if (strcmp (code_name, "reuse_rtx") == 0)
+    {
+      read_name (&name);
+      long idx = atoi (name.string);
+      /* Look it up by ID.  */
+      gcc_assert (idx < m_reuse_rtx_by_id.length ());
+      return_rtx = m_reuse_rtx_by_id[idx];
+      return return_rtx;
+    }
+
   /* 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);
@@ -1117,9 +1273,36 @@  rtx_reader::read_rtx_code (const char *code_name)
   memset (return_rtx, 0, RTX_CODE_SIZE (code));
   PUT_CODE (return_rtx, code);
 
+  if (reuse_id != -1)
+    {
+      /* Store away for later reuse.  */
+      m_reuse_rtx_by_id.safe_grow_cleared (reuse_id + 1);
+      m_reuse_rtx_by_id[reuse_id] = return_rtx;
+    }
+
   if (iterator)
     record_iterator_use (iterator, return_rtx);
 
+  /* Check for flags. */
+  read_flags (return_rtx);
+
+  /* Read REG_NOTE names for EXPR_LIST and INSN_LIST.  */
+  if ((GET_CODE (return_rtx) == EXPR_LIST
+       || GET_CODE (return_rtx) == INSN_LIST
+       || GET_CODE (return_rtx) == INT_LIST)
+      && !m_in_call_function_usage)
+    {
+      char ch = read_char ();
+      if (ch == ':')
+	{
+	  read_name (&name);
+	  PUT_MODE_RAW (return_rtx,
+			(machine_mode)parse_reg_note_name (name.string));
+	}
+      else
+	unread_char (ch);
+    }
+
   /* If what follows is `: mode ', read it and
      store the mode in the rtx.  */
 
@@ -1132,8 +1315,24 @@  rtx_reader::read_rtx_code (const char *code_name)
   else
     unread_char (c);
 
+  if (INSN_CHAIN_CODE_P (code))
+    {
+      read_name (&name);
+      INSN_UID (return_rtx) = atoi (name.string);
+    }
+
+  /* Use the format_ptr to parse the various operands of this rtx.
+     read_rtx_operand is a vfunc, allowing the parser to vary between
+     parsing .md files and parsing .rtl dumps; the function_reader subclass
+     overrides the defaults when loading RTL dumps, to handle the
+     various extra attributes emitted by print_rtx.  */
   for (int idx = 0; format_ptr[idx] != 0; idx++)
-    read_rtx_operand (return_rtx, idx);
+    return_rtx = read_rtx_operand (return_rtx, idx);
+
+  /* Call a vfunc to handle the various additional information that
+     print-rtl.c can write after the regular fields; does nothing when
+     parsing .md files.  */
+  handle_any_trailing_information (return_rtx);
 
   if (CONST_WIDE_INT_P (return_rtx))
     {
@@ -1197,9 +1396,11 @@  rtx_reader::read_rtx_code (const char *code_name)
 
 /* Subroutine of read_rtx_code.  Parse operand IDX within RETURN_RTX,
    based on the corresponding format character within GET_RTX_FORMAT
-   for the GET_CODE (RETURN_RTX).  */
+   for the GET_CODE (RETURN_RTX), and return RETURN_RTX.
+   This is a virtual function, so that function_reader can override
+   some parsing, and potentially return a different rtx.  */
 
-void
+rtx
 rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
 {
   RTX_CODE code = GET_CODE (return_rtx);
@@ -1217,6 +1418,9 @@  rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
       break;
 
     case 'e':
+      XEXP (return_rtx, idx) = read_nested_rtx ();
+      break;
+
     case 'u':
       XEXP (return_rtx, idx) = read_nested_rtx ();
       break;
@@ -1273,7 +1477,6 @@  rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
       {
 	char *stringbuf;
 	int star_if_braced;
-	struct obstack *string_obstack = get_string_obstack ();
 
 	c = read_skip_spaces ();
 	unread_char (c);
@@ -1293,7 +1496,10 @@  rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
 	star_if_braced = (format_ptr[idx] == 'T');
 
 	stringbuf = read_string (star_if_braced);
+	if (!stringbuf)
+	  break;
 
+#ifdef GENERATOR_FILE
 	/* For insn patterns, we want to provide a default name
 	   based on the file and line, like "*foo.md:12", if the
 	   given name is blank.  These are only for define_insn and
@@ -1303,6 +1509,7 @@  rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
 	    && (GET_CODE (return_rtx) == DEFINE_INSN
 		|| GET_CODE (return_rtx) == DEFINE_INSN_AND_SPLIT))
 	  {
+	    struct obstack *string_obstack = get_string_obstack ();
 	    char line_name[20];
 	    const char *read_md_filename = get_filename ();
 	    const char *fn = (read_md_filename ? read_md_filename : "rtx");
@@ -1348,11 +1555,23 @@  rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
 	    if (m != 0)
 	      record_iterator_use (m, return_rtx);
 	  }
+#endif /* #ifdef GENERATOR_FILE */
+
+	/* "stringbuf" was allocated within string_obstack and thus has
+	   the its lifetime restricted to that of the rtx_reader.  This is
+	   OK for the generator programs, but for non-generator programs,
+	   XSTR and XTMPL fields are meant to be allocated in the GC-managed
+	   heap.  Hence we need to allocate a copy in the GC-managed heap
+	   for the non-generator case.  */
+	const char *string_ptr = stringbuf;
+#ifndef GENERATOR_FILE
+	string_ptr = ggc_strdup (stringbuf);
+#endif /* #ifndef GENERATOR_FILE */
 
 	if (star_if_braced)
-	  XTMPL (return_rtx, idx) = stringbuf;
+	  XTMPL (return_rtx, idx) = string_ptr;
 	else
-	  XSTR (return_rtx, idx) = stringbuf;
+	  XSTR (return_rtx, idx) = string_ptr;
       }
       break;
 
@@ -1398,6 +1617,8 @@  rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
     default:
       gcc_unreachable ();
     }
+
+  return return_rtx;
 }
 
 /* Read a nested rtx construct from the MD file and return it.  */
@@ -1408,6 +1629,11 @@  rtx_reader::read_nested_rtx ()
   struct md_name name;
   rtx return_rtx;
 
+  /* In compact dumps, trailing "(nil)" values can be omitted.
+     Handle such dumps.  */
+  if (peek_char () == ')')
+    return NULL_RTX;
+
   require_char_ws ('(');
 
   read_name (&name);
@@ -1418,6 +1644,8 @@  rtx_reader::read_nested_rtx ()
 
   require_char_ws (')');
 
+  return_rtx = postprocess (return_rtx);
+
   return return_rtx;
 }
 
@@ -1454,8 +1682,9 @@  rtx_reader::read_rtx_variadic (rtx form)
 
 /* Constructor for class rtx_reader.  */
 
-rtx_reader::rtx_reader ()
-: md_reader ()
+rtx_reader::rtx_reader (bool compact)
+: md_reader (compact),
+  m_in_call_function_usage (false)
 {
   /* Set the global singleton pointer.  */
   rtx_reader_ptr = this;
diff --git a/gcc/rtl-tests.c b/gcc/rtl-tests.c
index 8edddfb..bd918a7 100644
--- a/gcc/rtl-tests.c
+++ b/gcc/rtl-tests.c
@@ -200,6 +200,7 @@  test_single_set ()
 static void
 test_uncond_jump ()
 {
+  set_new_first_and_last_insn (NULL, NULL);
   rtx_insn *label = gen_label_rtx ();
   rtx jump_pat = gen_rtx_SET (pc_rtx,
 			      gen_rtx_LABEL_REF (VOIDmode,
diff --git a/gcc/rtl.h b/gcc/rtl.h
index 7a44e3b..a9a63dc 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -3665,7 +3665,9 @@  extern void init_varasm_once (void);
 extern rtx make_debug_expr_from_rtl (const_rtx);
 
 /* In read-rtl.c */
+#ifdef GENERATOR_FILE
 extern bool read_rtx (const char *, vec<rtx> *);
+#endif
 
 /* In alias.c */
 extern rtx canon_rtx (rtx);
diff --git a/gcc/selftest-rtl.c b/gcc/selftest-rtl.c
new file mode 100644
index 0000000..8f3c976
--- /dev/null
+++ b/gcc/selftest-rtl.c
@@ -0,0 +1,82 @@ 
+/* 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 "memmodel.h"
+#include "emit-rtl.h"
+#include "selftest-rtl.h"
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Constructor for selftest::rtl_dump_test.
+   Read a dumped RTL function from PATH.
+   Takes ownership of PATH, freeing in dtor.
+   Use LOC as the effective location when reporting failures.  */
+
+rtl_dump_test::rtl_dump_test (const location &loc, char *path)
+  : m_path (path)
+{
+  /* Parse the tempfile.  */
+  auto_vec<const char *> argv (2);
+  argv.safe_push (progname);
+  argv.safe_push (path);
+  bool read_ok
+    = read_rtl_function_body (argv.length (), argv.address (), NULL, NULL);
+  ASSERT_TRUE_AT (loc, read_ok);
+}
+
+/* Destructor for selftest::rtl_dump_test.
+   Cleanup global state relating to the function, and free the path.  */
+
+selftest::rtl_dump_test::~rtl_dump_test ()
+{
+  /* Cleanups.  */
+  current_function_decl = NULL;
+  free_after_compilation (cfun);
+  set_cfun (NULL);
+  free (m_path);
+}
+
+/* Get the insn with the given uid, or NULL if not found.  */
+
+rtx_insn *
+get_insn_by_uid (int uid)
+{
+  for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    if (INSN_UID (insn) == uid)
+      return insn;
+
+  /* Not found.  */
+  return NULL;
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/selftest-rtl.h b/gcc/selftest-rtl.h
index f505018..35d6437 100644
--- a/gcc/selftest-rtl.h
+++ b/gcc/selftest-rtl.h
@@ -47,6 +47,25 @@  assert_rtl_dump_eq (const location &loc, const char *expected_dump, rtx x,
   assert_rtl_dump_eq (SELFTEST_LOCATION, (EXPECTED_DUMP), (RTX), \
 		      (REUSE_MANAGER))
 
+/* A class for testing RTL function dumps.  */
+
+class rtl_dump_test
+{
+ public:
+  /* Takes ownership of PATH.  */
+  rtl_dump_test (const location &loc, char *path);
+  ~rtl_dump_test ();
+
+ private:
+  char *m_path;
+};
+
+/* Get the insn with the given uid, or NULL if not found.  */
+
+extern rtx_insn *get_insn_by_uid (int uid);
+
+extern void verify_three_block_rtl_cfg (function *fun);
+
 } /* end of namespace selftest.  */
 
 #endif /* #if CHECKING_P */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index c1cd97e..bf2b84a 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -72,6 +72,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 c390873..d5afa6d 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -184,6 +184,7 @@  extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
 extern void pretty_print_c_tests ();
+extern void read_rtl_function_c_tests ();
 extern void rtl_tests_c_tests ();
 extern void selftest_c_tests ();
 extern void spellcheck_c_tests ();
diff --git a/gcc/testsuite/selftests/aarch64/times-two.rtl b/gcc/testsuite/selftests/aarch64/times-two.rtl
new file mode 100644
index 0000000..dbce67e
--- /dev/null
+++ b/gcc/testsuite/selftests/aarch64/times-two.rtl
@@ -0,0 +1,36 @@ 
+(function "times_two"
+  (insn-chain
+    (cnote 1 NOTE_INSN_DELETED)
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 4 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 2 (set (mem/c:SI (plus:DI (reg/f:DI virtual-stack-vars)
+                            (const_int -4)) [1 i+0 S4 A32])
+                    (reg:SI x0 [ i ])) "../../src/times-two.c":2
+                 (nil))
+      (cnote 3 NOTE_INSN_FUNCTION_BEG)
+      (cinsn 6 (set (reg:SI %2)
+                    (mem/c:SI (plus:DI (reg/f:DI virtual-stack-vars)
+                            (const_int -4)) [1 i+0 S4 A32])) "../../src/times-two.c":3
+                 (nil))
+      (cinsn 7 (set (reg:SI %0 [ _2 ])
+                    (ashift:SI (reg:SI %2)
+                        (const_int 1))) "../../src/times-two.c":3
+                 (nil))
+      (cinsn 10 (set (reg:SI %1 [ <retval> ])
+                    (reg:SI %0 [ _2 ])) "../../src/times-two.c":3
+                 (nil))
+      (cinsn 14 (set (reg/i:SI x0)
+                    (reg:SI %1 [ <retval> ])) "../../src/times-two.c":4
+                 (nil))
+      (cinsn 15 (use (reg/i:SI x0)) "../../src/times-two.c":4
+                 (nil))
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 2
+  ) ;; insn-chain
+  (crtl
+    (return_rtx 
+      (reg/i:SI x0)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "times_two"
diff --git a/gcc/testsuite/selftests/asr_div1.rtl b/gcc/testsuite/selftests/asr_div1.rtl
new file mode 100644
index 0000000..1507893
--- /dev/null
+++ b/gcc/testsuite/selftests/asr_div1.rtl
@@ -0,0 +1,24 @@ 
+;; Taken from
+;;    gcc/testsuite/gcc.dg/asr_div1.c -O2 -fdump-rtl-all -mtune=cortex-a53
+;; for aarch64, hand editing to the new format.
+
+(function "f1"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cinsn 1 (set (reg:DI %2)
+        (lshiftrt:DI (reg:DI %0)
+            (const_int 32)))
+        "../../src/gcc/testsuite/gcc.dg/asr_div1.c":14
+        (expr_list:REG_DEAD (reg:DI %0)
+          (nil)))
+      (cinsn 2 (set (reg:SI %1)
+        (ashiftrt:SI (subreg:SI (reg:DI %2) 0)
+            (const_int 3)))
+        "../../src/gcc/testsuite/gcc.dg/asr_div1.c":14
+        (expr_list:REG_DEAD (reg:DI %2)
+          (nil)))
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 2
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/bb-index.rtl b/gcc/testsuite/selftests/bb-index.rtl
new file mode 100644
index 0000000..7c66f22
--- /dev/null
+++ b/gcc/testsuite/selftests/bb-index.rtl
@@ -0,0 +1,8 @@ 
+(function "test_bb_index"
+  (insn-chain
+    (block 42
+      (edge-from entry (flags "FALLTHRU"))
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 42
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/cfg-test.rtl b/gcc/testsuite/selftests/cfg-test.rtl
new file mode 100644
index 0000000..08a0e22
--- /dev/null
+++ b/gcc/testsuite/selftests/cfg-test.rtl
@@ -0,0 +1,37 @@ 
+/* Example of a loading a CFG like this:
+       0  (entry)
+       |
+       2
+      / \
+     3   4
+      \ /
+       5
+       |
+       1  (exit).  */
+
+(function "cfg_test"
+  (insn-chain
+     (block 2
+       (edge-from entry (flags "FALLTHRU"))
+       (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+       (edge-to 3 (flags "TRUE_VALUE"))
+       (edge-to 4 (flags "FALSE_VALUE"))
+     ) ;; block 2
+     (block 3
+       (edge-from 2 (flags "TRUE_VALUE"))
+       (cnote 2 [bb 3] NOTE_INSN_BASIC_BLOCK)
+       (edge-to 5 (flags "FALLTHRU"))
+     ) ;; block 3
+     (block 4
+       (edge-from 2 (flags "FALSE_VALUE"))
+       (cnote 3 [bb 4] NOTE_INSN_BASIC_BLOCK)
+       (edge-to 5 (flags "FALLTHRU"))
+     ) ;; block 4
+     (block 5
+       (edge-from 3 (flags "FALLTHRU"))
+       (edge-from 4 (flags "FALLTHRU"))
+       (cnote 4 [bb 5] NOTE_INSN_BASIC_BLOCK)
+       (edge-to exit (flags "FALLTHRU"))
+     ) ;; block 5
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/const-int.rtl b/gcc/testsuite/selftests/const-int.rtl
new file mode 100644
index 0000000..e50dd88
--- /dev/null
+++ b/gcc/testsuite/selftests/const-int.rtl
@@ -0,0 +1,20 @@ 
+(function "const_int_examples"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cinsn 1
+        (set (reg:SI %0) (const_int 0))
+        "test.c":2 (nil))
+      (cinsn 2
+        (set (reg:SI %1) (const_int 1))
+        "test.c":2 (nil))
+      (cinsn 3
+        (set (reg:SI %2) (const_int -1))
+        "test.c":2 (nil))
+      (cinsn 4
+        (set (reg:SI %3) (const_int 256))
+        "test.c":2 (nil))
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 2
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/example-labels.rtl b/gcc/testsuite/selftests/example-labels.rtl
new file mode 100644
index 0000000..ec33bfd
--- /dev/null
+++ b/gcc/testsuite/selftests/example-labels.rtl
@@ -0,0 +1,8 @@ 
+(function "example_labels"
+  (insn-chain
+    (block 6
+      (clabel 100 30 (nil))
+      (clabel 200 40 ("some_label_name"))
+    ) ;; block 6
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/insn-with-mode.rtl b/gcc/testsuite/selftests/insn-with-mode.rtl
new file mode 100644
index 0000000..8c4609b
--- /dev/null
+++ b/gcc/testsuite/selftests/insn-with-mode.rtl
@@ -0,0 +1,7 @@ 
+(function "insn_with_mode"
+  (insn-chain
+    (block 2
+      (insn:TI 1 (set (reg:SI %0) (reg:SI %1)) (nil))
+    ) ;; block
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/jump-to-label-ref.rtl b/gcc/testsuite/selftests/jump-to-label-ref.rtl
new file mode 100644
index 0000000..29184bf
--- /dev/null
+++ b/gcc/testsuite/selftests/jump-to-label-ref.rtl
@@ -0,0 +1,17 @@ 
+(function "jump_to_label_ref"
+  (insn-chain
+    (block 4
+      (edge-from entry (flags "FALLTHRU"))
+      (cjump_insn 1 (set (pc) (label_ref 100))
+        "../../src/gcc/testsuite/rtl.dg/test.c":4)
+      (edge-to 5)
+    ) ;; block 4
+    (cbarrier 2)
+    (block 5   
+      (edge-from 4)
+      (clabel 100 2 (nil) [1 uses])
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 5
+  ) ;; insn-chain
+) ;; function
+
diff --git a/gcc/testsuite/selftests/jump-to-return.rtl b/gcc/testsuite/selftests/jump-to-return.rtl
new file mode 100644
index 0000000..9da89ef
--- /dev/null
+++ b/gcc/testsuite/selftests/jump-to-return.rtl
@@ -0,0 +1,11 @@ 
+(function "jump_to_return"
+  (insn-chain
+    (block 4
+      (edge-from entry (flags "FALLTHRU"))
+      (cjump_insn 1 (return)
+        "../../src/gcc/testsuite/rtl.dg/test.c":4
+        (nil))
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 4
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/jump-to-simple-return.rtl b/gcc/testsuite/selftests/jump-to-simple-return.rtl
new file mode 100644
index 0000000..5a9c1d5
--- /dev/null
+++ b/gcc/testsuite/selftests/jump-to-simple-return.rtl
@@ -0,0 +1,11 @@ 
+(function "jump_to_simple_return"
+  (insn-chain
+    (block 4
+      (edge-from entry (flags "FALLTHRU"))
+      (cjump_insn 1 (simple_return)
+        "../../src/gcc/testsuite/rtl.dg/test.c":4
+        (nil))
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 4
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/note-insn-deleted.rtl b/gcc/testsuite/selftests/note-insn-deleted.rtl
new file mode 100644
index 0000000..a388acd
--- /dev/null
+++ b/gcc/testsuite/selftests/note-insn-deleted.rtl
@@ -0,0 +1,5 @@ 
+(function "example_note"
+  (insn-chain
+    (cnote 1 NOTE_INSN_DELETED)
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/note_insn_basic_block.rtl b/gcc/testsuite/selftests/note_insn_basic_block.rtl
new file mode 100644
index 0000000..e792d98
--- /dev/null
+++ b/gcc/testsuite/selftests/note_insn_basic_block.rtl
@@ -0,0 +1,9 @@ 
+(function "example_of_note"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 2
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/simple-cse.rtl b/gcc/testsuite/selftests/simple-cse.rtl
new file mode 100644
index 0000000..5fe745d
--- /dev/null
+++ b/gcc/testsuite/selftests/simple-cse.rtl
@@ -0,0 +1,16 @@ 
+(function "test"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cinsn 1 (set (reg:SI %1)
+                    (plus:SI (reg:SI %0)
+                             (const_int 1))) (nil))
+      (cinsn 2 (set (reg:SI %2)
+                    (plus:SI (reg:SI %0)
+                             (const_int 1))) (nil))
+      (cinsn 3 (set (mem:SI (reg:SI %3) [1 i+0 S4 A32])
+                    (mult:SI (reg:SI %1) (reg:SI %2))) (nil))
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 2
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/symbol-ref.rtl b/gcc/testsuite/selftests/symbol-ref.rtl
new file mode 100644
index 0000000..8339eca
--- /dev/null
+++ b/gcc/testsuite/selftests/symbol-ref.rtl
@@ -0,0 +1,13 @@ 
+(function "example_of_symbol_ref"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cinsn 1
+        (set (reg:SI %0)
+             (high:SI (symbol_ref:SI ("isl_obj_map_vtable") [flags 0xc0] <var_decl 0x7fa0363ea240 isl_obj_map_vtable>)))
+        "y.c":12702
+        (nil))
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 2
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/x86_64/call-insn.rtl b/gcc/testsuite/selftests/x86_64/call-insn.rtl
new file mode 100644
index 0000000..8f3a781
--- /dev/null
+++ b/gcc/testsuite/selftests/x86_64/call-insn.rtl
@@ -0,0 +1,17 @@ 
+(function "test"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (ccall_insn/j 1
+        (set (reg:DF xmm0)
+             (call (mem:QI (symbol_ref:DI ("sqrt") [flags 0x41]  <function_decl 0x7f82b1429d00 sqrt>) [0 __builtin_sqrt S1 A8])
+                    (const_int 0))) "test.c":19
+        (expr_list:REG_CALL_DECL (symbol_ref:DI ("sqrt") [flags 0x41]  <function_decl 0x7f82b1429d00 sqrt>)
+           (expr_list:REG_EH_REGION (const_int 0)
+              (nil)))
+        (expr_list:DF (use (reg:DF xmm0))
+           (nil)))
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 2
+  ) ;; insn-chain
+) ;; function "test"
diff --git a/gcc/testsuite/selftests/x86_64/copy-hard-reg-into-frame.rtl b/gcc/testsuite/selftests/x86_64/copy-hard-reg-into-frame.rtl
new file mode 100644
index 0000000..4598a1c
--- /dev/null
+++ b/gcc/testsuite/selftests/x86_64/copy-hard-reg-into-frame.rtl
@@ -0,0 +1,15 @@ 
+(function "copy_hard_reg_into_frame"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cinsn 1 (set (mem/c:SI
+                      (plus:DI
+                        (reg/f:DI frame)
+                        (const_int -4))
+                      [1 i+0 S4 A32])
+               (reg:SI di [ i ])) "test.c":2
+               (nil))
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 2
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/testsuite/selftests/x86_64/times-two.rtl b/gcc/testsuite/selftests/x86_64/times-two.rtl
new file mode 100644
index 0000000..a26d888
--- /dev/null
+++ b/gcc/testsuite/selftests/x86_64/times-two.rtl
@@ -0,0 +1,51 @@ 
+;; Dump of this C function:
+;;
+;; int times_two (int i)
+;; {
+;;   return i * 2;
+;; }
+;;
+;; after expand for target==x86_64
+
+(function "times_two"
+  (insn-chain
+    (cnote 1 NOTE_INSN_DELETED)
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cnote 4 [bb 2] NOTE_INSN_BASIC_BLOCK)
+      (cinsn 2 (set (mem/c:SI (plus:DI (reg/f:DI virtual-stack-vars)
+                            (const_int -4)) [1 i+0 S4 A32])
+                    (reg:SI di [ i ])) "../../src/times-two.c":2
+                 (nil))
+      (cnote 3 NOTE_INSN_FUNCTION_BEG)
+      (cinsn 6 (set (reg:SI %2)
+                    (mem/c:SI (plus:DI (reg/f:DI virtual-stack-vars)
+                            (const_int -4)) [1 i+0 S4 A32])) "../../src/times-two.c":3
+                 (nil))
+      (cinsn 7 (parallel [
+                        (set (reg:SI %0 [ _2 ])
+                            (ashift:SI (reg:SI %2)
+                                (const_int 1)))
+                        (clobber (reg:CC flags))
+                    ]) "../../src/times-two.c":3
+                 (expr_list:REG_EQUAL (ashift:SI (mem/c:SI (plus:DI (reg/f:DI virtual-stack-vars)
+                                (const_int -4)) [1 i+0 S4 A32])
+                        (const_int 1))
+                    (nil)))
+      (cinsn 10 (set (reg:SI %1 [ <retval> ])
+                    (reg:SI %0 [ _2 ])) "../../src/times-two.c":3
+                 (nil))
+      (cinsn 14 (set (reg/i:SI ax)
+                    (reg:SI %1 [ <retval> ])) "../../src/times-two.c":4
+                 (nil))
+      (cinsn 15 (use (reg/i:SI ax)) "../../src/times-two.c":4
+                 (nil))
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 2
+  ) ;; insn-chain
+  (crtl
+    (return_rtx 
+      (reg/i:SI ax)
+    ) ;; return_rtx
+  ) ;; crtl
+) ;; function "times_two"
diff --git a/gcc/testsuite/selftests/x86_64/unspec.rtl b/gcc/testsuite/selftests/x86_64/unspec.rtl
new file mode 100644
index 0000000..ac822ac
--- /dev/null
+++ b/gcc/testsuite/selftests/x86_64/unspec.rtl
@@ -0,0 +1,20 @@ 
+(function "test_unspec"
+  (insn-chain
+    (block 2
+      (edge-from entry (flags "FALLTHRU"))
+      (cinsn 1 (set (mem/v:BLK (0|scratch:DI) [0  A8])
+                    (unspec:BLK [
+                            (mem/v:BLK (reuse_rtx 0) [0  A8])
+                        ] UNSPEC_MEMORY_BLOCKAGE)) "../../src/gcc/testsuite/gcc.dg/rtl/test.c":2
+                 (nil))
+
+      (cinsn 2 (set (mem/v:BLK (1|scratch:DI) [0  A8])
+                    (unspec_volatile:BLK [
+                            (mem/v:BLK (reuse_rtx 1) [0  A8])
+                        ] UNSPECV_RDTSCP)) "../../src/gcc/testsuite/gcc.dg/rtl/test.c":2
+                 (nil))
+
+      (edge-to exit (flags "FALLTHRU"))
+    ) ;; block 2
+  ) ;; insn-chain
+) ;; function
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index 0396feb..7ec62db 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 (VAR_P (var)
 	      || 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));