]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Introduce RTL function reader
authordmalcolm <dmalcolm@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 5 Jan 2017 19:32:09 +0000 (19:32 +0000)
committerdmalcolm <dmalcolm@138bc75d-0d04-0410-961f-82ee72b054a4>
Thu, 5 Jan 2017 19:32:09 +0000 (19:32 +0000)
This is the combination of these patches:
- [8a/9] Introduce class function_reader (v8)
- Add ASSERT_RTX_PTR_EQ
- [8b/9] Add target-independent selftests of RTL function reader (v2)
- [8c/9] Add aarch64-specific selftests for RTL function reader (v2)
- [8d/9] Add x86_64-specific selftests for RTL function reader (v2)

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.c (instantiate_decls): Guard call to
instantiate_decls_1 with if (DECL_INITIAL (fndecl)).
* function-tests.c (selftest::verify_three_block_rtl_cfg): Remove
"static".
* 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.
Wrap include of errors.h with #ifdef GENERATOR_FILE.
(have_error): New global, copied from errors.c.
(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".
(md_reader::read_md_files): Wrap with #ifdef GENERATOR_FILE.
(md_reader::read_file): New method.
* read-md.h (md_reader::md_reader): Add new param "compact".
(md_reader::read_file): New method.
(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::finalize_string): 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.
* selftest-rtl.c (selftest::assert_rtx_ptr_eq_at): New function.
* selftest-rtl.h (ASSERT_RTX_PTR_EQ): New macro.
(selftest::verify_three_block_rtl_cfg): New decl.
* 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.
(struct compact_insn_name): New struct.
(compact_insn_names): New array.
(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.
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'.  Call
finalize_string on XSTR and XTMPL fields.
(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.  Call
one_time_initialization.
* 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-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/asr_div1.rtl: New file.
* selftests/aarch64: New subdirectory.
* selftests/aarch64/times-two.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/mem.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.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@244110 138bc75d-0d04-0410-961f-82ee72b054a4

42 files changed:
gcc/ChangeLog
gcc/Makefile.in
gcc/config/aarch64/aarch64.c
gcc/config/i386/i386.c
gcc/emit-rtl.c
gcc/emit-rtl.h
gcc/function-tests.c
gcc/function.c
gcc/gensupport.c
gcc/print-rtl.c
gcc/read-md.c
gcc/read-md.h
gcc/read-rtl-function.c [new file with mode: 0644]
gcc/read-rtl-function.h [new file with mode: 0644]
gcc/read-rtl.c
gcc/rtl-tests.c
gcc/rtl.h
gcc/selftest-rtl.c [new file with mode: 0644]
gcc/selftest-rtl.h
gcc/selftest-run-tests.c
gcc/selftest.h
gcc/testsuite/ChangeLog
gcc/testsuite/selftests/aarch64/times-two.rtl [new file with mode: 0644]
gcc/testsuite/selftests/asr_div1.rtl [new file with mode: 0644]
gcc/testsuite/selftests/bb-index.rtl [new file with mode: 0644]
gcc/testsuite/selftests/cfg-test.rtl [new file with mode: 0644]
gcc/testsuite/selftests/const-int.rtl [new file with mode: 0644]
gcc/testsuite/selftests/example-labels.rtl [new file with mode: 0644]
gcc/testsuite/selftests/insn-with-mode.rtl [new file with mode: 0644]
gcc/testsuite/selftests/jump-to-label-ref.rtl [new file with mode: 0644]
gcc/testsuite/selftests/jump-to-return.rtl [new file with mode: 0644]
gcc/testsuite/selftests/jump-to-simple-return.rtl [new file with mode: 0644]
gcc/testsuite/selftests/mem.rtl [new file with mode: 0644]
gcc/testsuite/selftests/note-insn-deleted.rtl [new file with mode: 0644]
gcc/testsuite/selftests/note_insn_basic_block.rtl [new file with mode: 0644]
gcc/testsuite/selftests/simple-cse.rtl [new file with mode: 0644]
gcc/testsuite/selftests/symbol-ref.rtl [new file with mode: 0644]
gcc/testsuite/selftests/x86_64/call-insn.rtl [new file with mode: 0644]
gcc/testsuite/selftests/x86_64/copy-hard-reg-into-frame.rtl [new file with mode: 0644]
gcc/testsuite/selftests/x86_64/times-two.rtl [new file with mode: 0644]
gcc/testsuite/selftests/x86_64/unspec.rtl [new file with mode: 0644]
gcc/tree-dfa.c

index 9c46115f85c8789f1d9fc9342ccb9edfde040c49..1114d8a5f396752edb1fa724fac75c7790eadf88 100644 (file)
@@ -1,3 +1,121 @@
+2017-01-05  David Malcolm  <dmalcolm@redhat.com>
+
+       * 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.c (instantiate_decls): Guard call to
+       instantiate_decls_1 with if (DECL_INITIAL (fndecl)).
+       * function-tests.c (selftest::verify_three_block_rtl_cfg): Remove
+       "static".
+       * 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.
+       Wrap include of errors.h with #ifdef GENERATOR_FILE.
+       (have_error): New global, copied from errors.c.
+       (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".
+       (md_reader::read_md_files): Wrap with #ifdef GENERATOR_FILE.
+       (md_reader::read_file): New method.
+       * read-md.h (md_reader::md_reader): Add new param "compact".
+       (md_reader::read_file): New method.
+       (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::finalize_string): 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.
+       * selftest-rtl.c (selftest::assert_rtx_ptr_eq_at): New function.
+       * selftest-rtl.h (ASSERT_RTX_PTR_EQ): New macro.
+       (selftest::verify_three_block_rtl_cfg): New decl.
+       * 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.
+       (struct compact_insn_name): New struct.
+       (compact_insn_names): New array.
+       (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.
+       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'.  Call
+       finalize_string on XSTR and XTMPL fields.
+       (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.  Call
+       one_time_initialization.
+       * 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-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.
+
 2017-01-05  Uros Bizjak  <ubizjak@gmail.com>
 
        * config/i386/i386.md (*testqi_ext_3): No need to handle memory
index b9773f40c97fed9288667feebbceb24f70648430..c53c78a2f03f73ebe5ca4b96ed4f2254e9f5fcc7 100644 (file)
@@ -1420,6 +1420,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 \
@@ -1447,6 +1450,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 \
index 9dd75b07498871db3e37b335dc8a3e1d66ec1d8b..4a3cf18162200f35a5f945000628c8947d0a991f 100644 (file)
@@ -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"
@@ -14605,6 +14607,52 @@ aarch64_excess_precision (enum excess_precision_type type)
   return FLT_EVAL_METHOD_UNPREDICTABLE;
 }
 
+/* Target-specific selftests.  */
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftest for the RTL loader.
+   Verify that the RTL loader copes with a dump from
+   print_rtx_function.  This is essentially just a test that class
+   function_reader can handle a real dump, but it also verifies
+   that lookup_reg_by_dump_name correctly handles hard regs.
+   The presence of hard reg names in the dump means that the test is
+   target-specific, hence it is in this file.  */
+
+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
 
@@ -14977,6 +15025,11 @@ aarch64_libgcc_floating_mode_supported_p
 #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"
index 9516f8ff5820c731f90e6ddd58013b738616a9ce..3ad021a607f9d2288ca117b52685fb9fe0265bcc 100644 (file)
@@ -51206,6 +51206,209 @@ 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);
+         /* Verify that the "frame" REG was consolidated.  */
+         ASSERT_RTX_PTR_EQ (frame_pointer_rtx, 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 ()
+{
+  /* The test dump includes register "xmm0", where requires TARGET_SSE
+     to exist.  */
+  if (!TARGET_SSE)
+    return;
+
+  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
@@ -51213,6 +51416,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
index bfa42d2467fd858da2e5cb48915c1aec383f72c0..0b9552b3541e3b5c5336af5c61b642c66eb154ab 100644 (file)
@@ -1374,6 +1374,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;
+}
+
 \f
 /* 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
index 4f4747bbc337331d31eb0cfdfb65a01ea48a5f5b..da60a2d808c1742f65f0571c3422422292163906 100644 (file)
@@ -510,4 +510,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 */
index b29735cdc74fccbb59e672a64b7a8906b62d41db..ca30028143b262187b9d11cd0e65b26a2be81775 100644 (file)
@@ -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);
index e0eb550fa3ea7ab0fe3ccb51d92fdc3e8a3ab62a..7fde96adbe37470411e0136f29c9df101e18fa71 100644 (file)
@@ -1909,7 +1909,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))
index 261e0022ad839dca687002851a25362f7adcaeb6..cef0a0c83c8b0a7d434d6ce5586ab3eabee1eb69 100644 (file)
@@ -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 *);
 };
 
index 84d215b556d00be61613aeb82e8bf7847b937242..30fd7597450f99214dcf0f06b4436b4ef73bc5f3 100644 (file)
@@ -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;
index 7b73361830c11a12d45e92cda1fd3d1fcba6e3f0..ac28944d67150a8080c2950be346f8aa5dcda771 100644 (file)
@@ -17,14 +17,32 @@ 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"
+#ifdef GENERATOR_FILE
 #include "errors.h"
+#endif /* #ifdef GENERATOR_FILE */
 #include "statistics.h"
 #include "vec.h"
 #include "read-md.h"
 
+#ifndef GENERATOR_FILE
+
+/* Minimal reimplementation of errors.c for use by RTL frontend
+   within cc1.  */
+
+int have_error = 0;
+
+#endif /* #ifndef GENERATOR_FILE */
+
+
 /* Associates PTR (which can be a string, etc.) with the file location
    specified by FILENAME and LINENO.  */
 struct ptr_loc {
@@ -424,8 +442,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;
@@ -433,6 +451,8 @@ md_reader::read_name (struct md_name *name)
 
   c = read_skip_spaces ();
 
+  *out_loc = get_current_location ();
+
   i = 0;
   angle_bracket_depth = 0;
   while (1)
@@ -464,7 +484,7 @@ md_reader::read_name (struct md_name *name)
     }
 
   if (i == 0)
-    fatal_with_file_and_line ("missing name or number");
+    return false;
 
   name->buffer[i] = 0;
   name->string = name->buffer;
@@ -485,6 +505,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 +680,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 +982,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),
@@ -1129,6 +1188,8 @@ md_reader::add_include_path (const char *arg)
   m_last_dir_md_include_ptr = &dirtmp->next;
 }
 
+#ifdef GENERATOR_FILE
+
 /* The main routine for reading .md files.  Try to process all the .md
    files specified on the command line and return true if no error occurred.
 
@@ -1235,6 +1296,24 @@ md_reader::read_md_files (int argc, const char **argv,
   return !have_error;
 }
 
+#endif /* #ifdef GENERATOR_FILE */
+
+/* Read FILENAME.  */
+
+bool
+md_reader::read_file (const char *filename)
+{
+  m_read_md_filename = filename;
+  m_read_md_file = fopen (m_read_md_filename, "r");
+  if (m_read_md_file == 0)
+    {
+      perror (m_read_md_filename);
+      return false;
+    }
+  handle_toplevel_file ();
+  return !have_error;
+}
+
 /* class noop_reader : public md_reader */
 
 /* A dummy implementation which skips unknown directives.  */
index a66a74af13241337428d7438363818767a90830e..4fcbcb4b4e74463faa590423e297a4a3a88d511e 100644 (file)
@@ -106,10 +106,11 @@ 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 *));
+  bool read_file (const char *filename);
 
   /* A hook that handles a single .md-file directive, up to but not
      including the closing ')'.  It takes two arguments: the file position
@@ -119,10 +120,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 ();
@@ -179,7 +183,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;
@@ -247,7 +256,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 *);
@@ -261,14 +270,30 @@ 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; }
+
+  /* Hook to allow function_reader subclass to put STRINGBUF into gc-managed
+     memory, rather than within an obstack.
+     This base class implementation is a no-op.  */
+  virtual const char *finalize_string (char *stringbuf) { return stringbuf; }
+
+ 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 (file)
index 0000000..c5cb3f7
--- /dev/null
@@ -0,0 +1,2123 @@
+/* read-rtl-function.c - Reader for RTL function dumps
+   Copyright (C) 2016-2017 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 "diagnostic.h"
+#include "read-md.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 "varasm.h"
+#include "read-rtl-function.h"
+#include "selftest.h"
+#include "selftest-rtl.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 ();
+
+  /* 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 x, int idx) FINAL OVERRIDE;
+  void handle_any_trailing_information (rtx x) FINAL OVERRIDE;
+  rtx postprocess (rtx) FINAL OVERRIDE;
+  const char *finalize_string (char *stringbuf) 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 x, int idx);
+  void read_rtx_operand_i_or_n (rtx x, int idx, char format_char);
+  rtx read_rtx_operand_r (rtx x);
+  void extra_parsing_for_operand_code_0 (rtx x, 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 *insn,
+                                 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 (rtx_insn *insn);
+
+  void handle_insn_uids ();
+  void apply_fixups ();
+
+ private:
+  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:
+  /* Constructor for a fixup at LOC affecting X.  */
+  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:
+  /* Constructor for a fixup at LOC affecting INSN's operand
+     with index OPERAND_IDX.  */
+  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 based on an integer UID.  */
+
+class fixup_insn_uid : public operand_fixup
+{
+ public:
+  /* Constructor for a fixup at LOC affecting INSN's operand
+     with index OPERAND_IDX.  Record INSN_UID as the uid.  */
+  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 operand of INSN with
+   index OPERAND_IDX.  */
+
+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 based on an integer UID, as read by READER.  */
+
+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 *) 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
+   read by READER.  */
+
+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 '< and '>' wrapping the regno,
+     offseting it by (LAST_VIRTUAL_REGISTER + 1), so that the
+     first non-virtual pseudo is dumped as "<0>".  */
+  if (name[0] == '<' && name[strlen (name) - 1] == '>')
+    {
+      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 ()
+: rtx_reader (true),
+  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,
+   for parsing the remainder of a directive with name NAME
+   seen at START_LOC.
+
+   Require a top-level "function" directive, 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 ()
+{
+  /* We start in cfgrtl mode, rather than cfglayout mode.  */
+  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::finalize_function (fndecl, false);
+
+  /* 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);
+
+}
+
+/* Look within the the params of FNDECL for a param named NAME.
+   Return NULL_TREE if one isn't found.  */
+
+static tree
+find_param_by_name (tree fndecl, const char *name)
+{
+  for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
+    if (strcmp (name, IDENTIFIER_POINTER (DECL_NAME (arg))) == 0)
+      return arg;
+  return NULL_TREE;
+}
+
+/* Parse the content of a "param" directive, having already parsed the
+   "(param".  Consume the trailing ')'.  */
+
+void
+function_reader::parse_param ()
+{
+  require_char_ws ('"');
+  file_location loc = get_current_location ();
+  char *name = read_quoted_string ();
+
+  /* Lookup param by name.  */
+  tree t_param = find_param_by_name (cfun->decl, name);
+  if (!t_param)
+    fatal_at (loc, "param not found: %s", name);
+
+  /* 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 TOK, 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.
+   FROM is true for an "edge-from" directive, false for an "edge-to"
+   directive.  */
+
+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 (')');
+
+  /* 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.
+     FIXME: 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 an rtx instruction, having parsed the opening and parenthesis, and
+   name NAME, seen at START_LOC, 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
+   at location LOC.
+   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 (')');
+}
+
+/* Parse operand IDX of X, returning X, or an equivalent rtx
+   expression (for consolidating singletons).
+   This is an 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 x, int idx)
+{
+  RTX_CODE code = GET_CODE (x);
+  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 (x))
+       {
+         m_in_call_function_usage = true;
+         return rtx_reader::read_rtx_operand (x, idx);
+         m_in_call_function_usage = false;
+       }
+      else
+       return rtx_reader::read_rtx_operand (x, idx);
+      break;
+
+    case 'u':
+      read_rtx_operand_u (x, idx);
+      /* Don't run regular parser for 'u'.  */
+      return x;
+
+    case 'i':
+    case 'n':
+      read_rtx_operand_i_or_n (x, idx, format_char);
+      /* Don't run regular parser for these codes.  */
+      return x;
+
+    case 'B':
+      gcc_assert (is_compact ());
+      /* Compact mode doesn't store BBs.  */
+      /* Don't run regular parser.  */
+      return x;
+
+    case 'r':
+      /* Don't run regular parser for 'r'.  */
+      return read_rtx_operand_r (x);
+
+    default:
+      break;
+    }
+
+  /* Call base class implementation.  */
+  x = rtx_reader::read_rtx_operand (x, idx);
+
+  /* Handle any additional parsing needed to handle what the dump
+     could contain.  */
+  switch (format_char)
+    {
+    case '0':
+      extra_parsing_for_operand_code_0 (x, idx);
+      break;
+
+    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 x;
+}
+
+/* Parse operand IDX of X, of code 'u', when 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 x, int idx)
+{
+  /* In compact mode, the PREV/NEXT insn uids are not dumped, so skip
+     the "uu" when reading. */
+  if (is_compact () && GET_CODE (x) != 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, x, 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;
+}
+
+/* Parse operand IDX of X, of code 'i' or 'n' (as specified by FORMAT_CHAR).
+   Special-cased handling of these, for reading function dumps.  */
+
+void
+function_reader::read_rtx_operand_i_or_n (rtx x, 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 (x))
+    return;
+
+  if (idx == 4 && INSN_P (x))
+    {
+      maybe_read_location (as_a <rtx_insn *> (x));
+      return;
+    }
+
+  /* INSN_CODEs aren't printed in compact mode, so don't attempt to
+     parse them.  */
+  if (is_compact ()
+      && INSN_P (x)
+      && &INSN_CODE (x) == &XINT (x, idx))
+    {
+      INSN_CODE (x) = -1;
+      return;
+    }
+
+  /* Handle UNSPEC and UNSPEC_VOLATILE's operand 1.  */
+#if !defined(GENERATOR_FILE) && NUM_UNSPECV_VALUES > 0
+  if (idx == 1
+      && GET_CODE (x) == UNSPEC_VOLATILE)
+    {
+      XINT (x, 1)
+       = parse_enum_value (NUM_UNSPECV_VALUES, unspecv_strings);
+      return;
+    }
+#endif
+#if !defined(GENERATOR_FILE) && NUM_UNSPEC_VALUES > 0
+  if (idx == 1
+      && (GET_CODE (x) == UNSPEC
+         || GET_CODE (x) == UNSPEC_VOLATILE))
+    {
+      XINT (x, 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 (x, idx) = value;
+}
+
+/* Parse the 'r' operand of X, returning X, or an equivalent rtx
+   expression (for consolidating singletons).
+   Special-cased handling of code 'r' for reading function dumps.  */
+
+rtx
+function_reader::read_rtx_operand_r (rtx x)
+{
+  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 (x, regno, 1);
+
+  /* Consolidate singletons.  */
+  x = consolidate_singletons (x);
+
+  ORIGINAL_REGNO (x) = 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 (x) = atoi (desc_start);
+       }
+      else
+       {
+         /* Assume we have REG_EXPR.  */
+         add_fixup_expr (loc, x, desc_start);
+       }
+      free (desc);
+    }
+  else
+    unread_char (ch);
+  if (expect_original_regno)
+    {
+      require_char_ws ('[');
+      char *desc = read_until ("]", true);
+      ORIGINAL_REGNO (x) = atoi (desc);
+      free (desc);
+    }
+
+  return x;
+}
+
+/* Additional parsing for format code '0' in dumps, handling a variety
+   of special-cases in print_rtx, when parsing operand IDX of X.  */
+
+void
+function_reader::extra_parsing_for_operand_code_0 (rtx x, int idx)
+{
+  RTX_CODE code = GET_CODE (x);
+  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 (x) = strtol (name.string, NULL, 16);
+
+         /* We can't reconstruct SYMBOL_REF_BLOCK; set it to NULL.  */
+         if (SYMBOL_REF_HAS_BLOCK_INFO_P (x))
+           SYMBOL_REF_BLOCK (x) = NULL;
+
+         require_char (']');
+       }
+      else
+       unread_char (c);
+
+      /* 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.  */
+      c = read_skip_spaces ();
+      if (c == '<')
+       {
+         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, x, idx,
+                                          bb_idx);
+         require_char_ws (']');
+       }
+      else
+       unread_char (c);
+    }
+}
+
+/* Implementation of rtx_reader::handle_any_trailing_information.
+   Handle the various additional information that print-rtl.c can
+   write after the regular fields, when parsing X.  */
+
+void
+function_reader::handle_any_trailing_information (rtx x)
+{
+  struct md_name name;
+
+  switch (GET_CODE (x))
+    {
+      case MEM:
+       {
+         int ch;
+         require_char_ws ('[');
+         read_name (&name);
+         set_mem_alias_set (x, 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 (x), desc);
+             free (desc);
+           }
+         else
+           read_char ();
+
+         /* We may optionally have '+' for MEM_OFFSET_KNOWN_P.  */
+         ch = read_skip_spaces ();
+         if (ch == '+')
+           {
+             read_name (&name);
+             set_mem_offset (x, atoi (name.string));
+           }
+         else
+           unread_char (ch);
+
+         /* Handle optional " S" for MEM_SIZE.  */
+         ch = read_skip_spaces ();
+         if (ch == 'S')
+           {
+             read_name (&name);
+             set_mem_size (x, 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);
+             set_mem_align (x, atoi (name.string));
+           }
+         else
+           unread_char (ch);
+
+         /* Handle optional " AS" for MEM_ADDR_SPACE.  */
+         ch = read_skip_spaces ();
+         if (ch == 'A' && peek_char () == 'S')
+           {
+             read_char ();
+             read_name (&name);
+             set_mem_addr_space (x, 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 a MEM_EXPR in DESC and turn it back into a tree.
+   We handle "<retval>" and param names within cfun, 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);
+
+  tree param = find_param_by_name (fndecl, desc);
+  if (param)
+    return param;
+
+  /* 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 mimicking 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 that at LOC we saw an insn uid INSN_UID for the operand with index
+   OPERAND_IDX within INSN, so that the pointer value can be fixed up in
+   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 that at LOC we saw an basic block index BB_IDX for the operand with index
+   OPERAND_IDX within INSN, so that the pointer value can be fixed up in
+   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));
+}
+
+/* Placeholder hook for recording source location information seen in a dump.
+   This is empty for now.  */
+
+void
+function_reader::add_fixup_source_location (file_location, rtx_insn *,
+                                           const char *, int)
+{
+}
+
+/* Record that at LOC we saw textual description DESC of the MEM_EXPR or REG_EXPR
+   of INSN, so that the fields can be fixed up in 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.  Return the global rtx for
+   the register with regno REGNO.  */
+
+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;
+}
+
+/* Ensure that the backend can cope with a REG with regno REGNO.
+   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)
+{
+  if (reg_rtx_no < regno + 1)
+    reg_rtx_no = regno + 1;
+
+  crtl->emit.ensure_regno_capacity ();
+  gcc_assert (regno < crtl->emit.regno_pointer_align_length);
+}
+
+/* Helper function for consolidate_singletons, for handling REG instances.
+   Given REG instance X of some regno, return the singleton rtx for that
+   regno, if it exists, or X.  */
+
+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.
+
+   Return the equivalent singleton rtx for X, if any, otherwise X.  */
+
+rtx
+function_reader::consolidate_singletons (rtx x)
+{
+  if (!x)
+    return x;
+
+  switch (GET_CODE (x))
+    {
+    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.
+   Return the equivalent singleton rtx for X, if any, otherwise X.  */
+
+rtx
+function_reader::postprocess (rtx x)
+{
+  return consolidate_singletons (x);
+}
+
+/* Implementation of rtx_reader::finalize_string for reading function dumps.
+   Make a GC-managed copy of STRINGBUF.  */
+
+const char *
+function_reader::finalize_string (char *stringbuf)
+{
+  return ggc_strdup (stringbuf);
+}
+
+/* Attempt to parse optional location information for insn INSN, as
+   potentially written out by rtx_writer::print_rtx_operand_code_i.
+   We look for a quoted string followed by a colon.  */
+
+void
+function_reader::maybe_read_location (rtx_insn *insn)
+{
+  file_location loc = get_current_location ();
+
+  /* Attempt to parse a quoted string.  */
+  int ch = read_skip_spaces ();
+  if (ch == '"')
+    {
+      char *filename = read_quoted_string ();
+      require_char (':');
+      struct md_name line_num;
+      read_name (&line_num);
+      add_fixup_source_location (loc, insn, filename, atoi (line_num.string));
+    }
+  else
+    unread_char (ch);
+}
+
+/* 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, parsing a dump located at PATH.
+   Return true iff the file was successfully parsed.  */
+
+bool
+read_rtl_function_body (const char *path)
+{
+  initialize_rtl ();
+  init_emit ();
+  init_varasm_status ();
+
+  function_reader reader;
+  if (!reader.read_file (path))
+    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 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 edge E is as expected, with the src and dest basic blocks
+   having indices EXPECTED_SRC_IDX and EXPECTED_DEST_IDX respectively, and
+   the edge having flags equal to EXPECTED_FLAGS.
+   Use 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 edge EDGE is as expected, with the src and dest basic blocks
+   having indices EXPECTED_SRC_IDX and EXPECTED_DEST_IDX respectively, and
+   the edge having flags equal to EXPECTED_FLAGS.  */
+
+#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_RTX_PTR_EQ (pc_rtx, SET_DEST (PATTERN (jump_insn)));
+  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_RTX_PTR_EQ (ret_rtx, JUMP_LABEL (jump_insn));
+}
+
+/* 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_RTX_PTR_EQ (simple_return_rtx, JUMP_LABEL (jump_insn));
+}
+
+/* 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);
+}
+
+/* Verify that function_reader::handle_any_trailing_information correctly
+   parses all the possible items emitted for a MEM.  */
+
+static void
+test_loading_mem ()
+{
+  rtl_dump_test t (SELFTEST_LOCATION, locate_file ("mem.rtl"));
+
+  ASSERT_STREQ ("test_mem", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+  ASSERT_TRUE (cfun);
+
+  /* Verify parsing of "[42 i+17 S8 A128 AS5]".  */
+  rtx_insn *insn_1 = get_insn_by_uid (1);
+  rtx set1 = single_set (insn_1);
+  rtx mem1 = SET_DEST (set1);
+  ASSERT_EQ (42, MEM_ALIAS_SET (mem1));
+  /* "+17".  */
+  ASSERT_TRUE (MEM_OFFSET_KNOWN_P (mem1));
+  ASSERT_EQ (17, MEM_OFFSET (mem1));
+  /* "S8".  */
+  ASSERT_EQ (8, MEM_SIZE (mem1));
+  /* "A128.  */
+  ASSERT_EQ (128, MEM_ALIGN (mem1));
+  /* "AS5.  */
+  ASSERT_EQ (5, MEM_ADDR_SPACE (mem1));
+
+  /* Verify parsing of "43 i+18 S9 AS6"
+     (an address space without an alignment).  */
+  rtx_insn *insn_2 = get_insn_by_uid (2);
+  rtx set2 = single_set (insn_2);
+  rtx mem2 = SET_DEST (set2);
+  ASSERT_EQ (43, MEM_ALIAS_SET (mem2));
+  /* "+18".  */
+  ASSERT_TRUE (MEM_OFFSET_KNOWN_P (mem2));
+  ASSERT_EQ (18, MEM_OFFSET (mem2));
+  /* "S9".  */
+  ASSERT_EQ (9, MEM_SIZE (mem2));
+  /* "AS6.  */
+  ASSERT_EQ (6, MEM_ADDR_SPACE (mem2));
+}
+
+/* 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 ();
+  test_loading_mem ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/read-rtl-function.h b/gcc/read-rtl-function.h
new file mode 100644 (file)
index 0000000..45ada84
--- /dev/null
@@ -0,0 +1,25 @@
+/* read-rtl-function.h - Reader for RTL function dumps
+   Copyright (C) 2016-2017 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
+
+extern bool read_rtl_function_body (const char *path);
+
+#endif /* GCC_READ_RTL_FUNCTION_H */
index a08ce5c9f080057452982893e05b31e8a57f6d44..e9c806828003968c2bb32df198cdb0ef00f35b5f 100644 (file)
@@ -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;
@@ -142,6 +155,25 @@ apply_mode_iterator (void *loc, int mode)
   PUT_MODE ((rtx) loc, (machine_mode) mode);
 }
 
+/* In compact dumps, the code of insns is prefixed with "c", giving "cinsn",
+   "cnote" etc, and CODE_LABEL is special-cased as "clabel".  */
+
+struct compact_insn_name {
+  RTX_CODE code;
+  const char *name;
+};
+
+static const compact_insn_name compact_insn_names[] = {
+  { DEBUG_INSN, "cdebug_insn" },
+  { INSN, "cinsn" },
+  { JUMP_INSN, "cjump_insn" },
+  { CALL_INSN, "ccall_insn" },
+  { JUMP_TABLE_DATA, "cjump_table_data" },
+  { BARRIER, "cbarrier" },
+  { CODE_LABEL, "clabel" },
+  { NOTE, "cnote" }
+};
+
 /* Implementations of the iterator_group callbacks for codes.  */
 
 static int
@@ -153,6 +185,10 @@ find_code (const char *name)
     if (strcmp (GET_RTX_NAME (i), name) == 0)
       return i;
 
+  for (i = 0; i < (signed)ARRAY_SIZE (compact_insn_names); i++)
+    if (strcmp (compact_insn_names[i].name, name) == 0)
+      return compact_insn_names[i].code;
+
   fatal_with_file_and_line ("unknown rtx code `%s'", name);
 }
 
@@ -181,6 +217,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 +290,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 +458,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 +623,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 +698,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 +769,8 @@ atoll (const char *p)
 }
 #endif
 \f
+
+#ifdef GENERATOR_FILE
 /* Process a define_conditions directive, starting with the optional
    space after the "define_conditions".  The directive looks like this:
 
@@ -765,6 +812,7 @@ md_reader::read_conditions ()
       add_c_test (expr, value);
     }
 }
+#endif /* #ifdef GENERATOR_FILE */
 
 static void
 validate_const_int (const char *string)
@@ -861,6 +909,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,15 +1070,6 @@ 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;
-    }
-
   /* Handle various rtx-related declarations that aren't themselves
      encoded as rtxes.  */
   if (strcmp (rtx_name, "define_conditions") == 0)
@@ -1082,6 +1123,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 +1228,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 +1242,37 @@ rtx_reader::read_rtx_code (const char *code_name)
       rtx value;               /* Value of this node.  */
     };
 
+  /* 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 +1280,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 +1322,19 @@ 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.  */
   for (int idx = 0; format_ptr[idx] != 0; idx++)
-    read_rtx_operand (return_rtx, idx);
+    return_rtx = read_rtx_operand (return_rtx, idx);
+
+  /* Handle any additional information that after the regular fields
+     (e.g. when parsing function dumps).  */
+  handle_any_trailing_information (return_rtx);
 
   if (CONST_WIDE_INT_P (return_rtx))
     {
@@ -1197,9 +1398,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 +1420,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 +1479,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 +1498,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 +1511,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 +1557,14 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
            if (m != 0)
              record_iterator_use (m, return_rtx);
          }
+#endif /* #ifdef GENERATOR_FILE */
+
+       const char *string_ptr = finalize_string (stringbuf);
 
        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 +1610,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 +1622,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 +1637,8 @@ rtx_reader::read_nested_rtx ()
 
   require_char_ws (')');
 
+  return_rtx = postprocess (return_rtx);
+
   return return_rtx;
 }
 
@@ -1454,11 +1675,14 @@ 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;
+
+  one_time_initialization ();
 }
 
 /* Destructor for class rtx_reader.  */
index 044a6f174575f6fbad99ce976eea97e57edffdaf..705434012d6be41b4877f150a6aceb06046afe75 100644 (file)
@@ -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,
index b9a7989c0d7e621aa2e657802920aa5a50413d05..638dfc8b1cb665f78919deaac486e433fb115d09 100644 (file)
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -3712,7 +3712,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 (file)
index 0000000..bfb1ab7
--- /dev/null
@@ -0,0 +1,100 @@
+/* Selftest support for RTL.
+   Copyright (C) 2016-2017 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 {
+
+/* Compare rtx EXPECTED and ACTUAL by pointer equality, calling
+   ::selftest::pass if they are equal, aborting if they are non-equal.
+   LOC is the effective location of the assertion, MSG describes it.  */
+
+void
+assert_rtx_ptr_eq_at (const location &loc, const char *msg,
+                     rtx expected, rtx actual)
+{
+  if (expected == actual)
+    ::selftest::pass (loc, msg);
+  else
+    {
+      fprintf (stderr, "%s:%i: %s: FAIL: %s\n", loc.m_file, loc.m_line,
+              loc.m_function, msg);
+      fprintf (stderr, "  expected (at %p): ", (void *)expected);
+      print_rtl (stderr, expected);
+      fprintf (stderr, "\n  actual (at %p): ", (void *)actual);
+      print_rtl (stderr, actual);
+      fprintf (stderr, "\n");
+      abort ();
+    }
+}
+
+/* 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)
+{
+  bool read_ok = read_rtl_function_body (path);
+  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 */
index 434dfa17183c961f9ad11488fe46c15160e68604..2218f9c528022dd14d5c2b2dc2b8632b5e9534e0 100644 (file)
@@ -47,6 +47,43 @@ assert_rtl_dump_eq (const location &loc, const char *expected_dump, rtx x,
   assert_rtl_dump_eq (SELFTEST_LOCATION, (EXPECTED_DUMP), (RTX), \
                      (REUSE_MANAGER))
 
+/* Evaluate rtx EXPECTED and ACTUAL and compare them with ==
+   (i.e. pointer equality), calling ::selftest::pass if they are
+   equal, aborting if they are non-equal.  */
+
+#define ASSERT_RTX_PTR_EQ(EXPECTED, ACTUAL) \
+  SELFTEST_BEGIN_STMT                                                  \
+  const char *desc = "ASSERT_RTX_PTR_EQ (" #EXPECTED ", " #ACTUAL ")";  \
+  ::selftest::assert_rtx_ptr_eq_at (SELFTEST_LOCATION, desc, (EXPECTED), \
+                                   (ACTUAL));                          \
+  SELFTEST_END_STMT
+
+/* Compare rtx EXPECTED and ACTUAL by pointer equality, calling
+   ::selftest::pass if they are equal, aborting if they are non-equal.
+   LOC is the effective location of the assertion, MSG describes it.  */
+
+extern void assert_rtx_ptr_eq_at (const location &loc, const char *msg,
+                                 rtx expected, rtx actual);
+
+/* 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 */
index 8bc6b0e5f261202a376a334fa51ff0fe6c25c02e..f62bc72b07207cf5e4cacb1b3831604c0e69b8b8 100644 (file)
@@ -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.  */
index d13861c0426233f2ba27193cba342f375026662f..dad53e9fe09f2258efabb04f85b03ffd1bb5b097 100644 (file)
@@ -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 ();
index aa6dc348cc390639ae178fb746bcb613075c071a..7b11cf5b60ee35bc5962d8fb74c956aa2b3e9791 100644 (file)
@@ -1,3 +1,27 @@
+2017-01-05  David Malcolm  <dmalcolm@redhat.com>
+
+       * selftests/asr_div1.rtl: New file.
+       * selftests/aarch64: New subdirectory.
+       * selftests/aarch64/times-two.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/mem.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.
+
 2017-01-05  Nathan Sidwell  <nathan@acm.org>
 
        PR c++/78765
diff --git a/gcc/testsuite/selftests/aarch64/times-two.rtl b/gcc/testsuite/selftests/aarch64/times-two.rtl
new file mode 100644 (file)
index 0000000..05e2bdb
--- /dev/null
@@ -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 (file)
index 0000000..2b03b38
--- /dev/null
@@ -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 (file)
index 0000000..7c66f22
--- /dev/null
@@ -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 (file)
index 0000000..08a0e22
--- /dev/null
@@ -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 (file)
index 0000000..477f156
--- /dev/null
@@ -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 (file)
index 0000000..ec33bfd
--- /dev/null
@@ -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 (file)
index 0000000..7e8f2a8
--- /dev/null
@@ -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 (file)
index 0000000..29184bf
--- /dev/null
@@ -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 (file)
index 0000000..9da89ef
--- /dev/null
@@ -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 (file)
index 0000000..5a9c1d5
--- /dev/null
@@ -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/mem.rtl b/gcc/testsuite/selftests/mem.rtl
new file mode 100644 (file)
index 0000000..219a4aa
--- /dev/null
@@ -0,0 +1,9 @@
+(function "test_mem"
+  (insn-chain
+    (block 2
+      ;; Various nonsensical values, to exercise the parser:
+      (cinsn 1 (set (mem:SI (reg:SI <10>) [42 i+17 S8 A128 AS5]) (reg:SI <1>)))
+      (cinsn 2 (set (mem:SI (reg:SI <11>) [43 i+18 S9 AS6]) (reg:SI <2>)))
+    ) ;; block 6
+  ) ;; 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 (file)
index 0000000..a388acd
--- /dev/null
@@ -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 (file)
index 0000000..e792d98
--- /dev/null
@@ -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 (file)
index 0000000..2dae4ae
--- /dev/null
@@ -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 (file)
index 0000000..98b583e
--- /dev/null
@@ -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 (file)
index 0000000..8f3a781
--- /dev/null
@@ -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 (file)
index 0000000..4598a1c
--- /dev/null
@@ -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 (file)
index 0000000..8cec47a
--- /dev/null
@@ -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 (file)
index 0000000..ac822ac
--- /dev/null
@@ -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
index 4f95682083b40ccb34bf9cd3b0dc190161cf3c65..4e47be1dc05ee7f9cbf3bc4f8b81e70d57842c91 100644 (file)
@@ -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));