From 51b861137ed73ae73e15a136949301fbaaf89202 Mon Sep 17 00:00:00 2001 From: David Malcolm Date: Thu, 5 Jan 2017 19:32:09 +0000 Subject: [PATCH] Introduce RTL function reader 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. From-SVN: r244110 --- gcc/ChangeLog | 118 + gcc/Makefile.in | 4 + gcc/config/aarch64/aarch64.c | 53 + gcc/config/i386/i386.c | 210 ++ gcc/emit-rtl.c | 13 + gcc/emit-rtl.h | 2 + gcc/function-tests.c | 2 +- gcc/function.c | 3 +- gcc/gensupport.c | 2 +- gcc/print-rtl.c | 2 +- gcc/read-md.c | 89 +- gcc/read-md.h | 35 +- gcc/read-rtl-function.c | 2123 +++++++++++++++++ gcc/read-rtl-function.h | 25 + gcc/read-rtl.c | 260 +- gcc/rtl-tests.c | 1 + gcc/rtl.h | 2 + gcc/selftest-rtl.c | 100 + gcc/selftest-rtl.h | 37 + gcc/selftest-run-tests.c | 1 + gcc/selftest.h | 1 + gcc/testsuite/ChangeLog | 24 + gcc/testsuite/selftests/aarch64/times-two.rtl | 36 + gcc/testsuite/selftests/asr_div1.rtl | 24 + gcc/testsuite/selftests/bb-index.rtl | 8 + gcc/testsuite/selftests/cfg-test.rtl | 37 + gcc/testsuite/selftests/const-int.rtl | 20 + gcc/testsuite/selftests/example-labels.rtl | 8 + gcc/testsuite/selftests/insn-with-mode.rtl | 7 + gcc/testsuite/selftests/jump-to-label-ref.rtl | 17 + gcc/testsuite/selftests/jump-to-return.rtl | 11 + .../selftests/jump-to-simple-return.rtl | 11 + gcc/testsuite/selftests/mem.rtl | 9 + gcc/testsuite/selftests/note-insn-deleted.rtl | 5 + .../selftests/note_insn_basic_block.rtl | 9 + gcc/testsuite/selftests/simple-cse.rtl | 16 + gcc/testsuite/selftests/symbol-ref.rtl | 13 + gcc/testsuite/selftests/x86_64/call-insn.rtl | 17 + .../x86_64/copy-hard-reg-into-frame.rtl | 15 + gcc/testsuite/selftests/x86_64/times-two.rtl | 51 + gcc/testsuite/selftests/x86_64/unspec.rtl | 20 + gcc/tree-dfa.c | 5 + 42 files changed, 3414 insertions(+), 32 deletions(-) create mode 100644 gcc/read-rtl-function.c create mode 100644 gcc/read-rtl-function.h create mode 100644 gcc/selftest-rtl.c create mode 100644 gcc/testsuite/selftests/aarch64/times-two.rtl create mode 100644 gcc/testsuite/selftests/asr_div1.rtl create mode 100644 gcc/testsuite/selftests/bb-index.rtl create mode 100644 gcc/testsuite/selftests/cfg-test.rtl create mode 100644 gcc/testsuite/selftests/const-int.rtl create mode 100644 gcc/testsuite/selftests/example-labels.rtl create mode 100644 gcc/testsuite/selftests/insn-with-mode.rtl create mode 100644 gcc/testsuite/selftests/jump-to-label-ref.rtl create mode 100644 gcc/testsuite/selftests/jump-to-return.rtl create mode 100644 gcc/testsuite/selftests/jump-to-simple-return.rtl create mode 100644 gcc/testsuite/selftests/mem.rtl create mode 100644 gcc/testsuite/selftests/note-insn-deleted.rtl create mode 100644 gcc/testsuite/selftests/note_insn_basic_block.rtl create mode 100644 gcc/testsuite/selftests/simple-cse.rtl create mode 100644 gcc/testsuite/selftests/symbol-ref.rtl create mode 100644 gcc/testsuite/selftests/x86_64/call-insn.rtl create mode 100644 gcc/testsuite/selftests/x86_64/copy-hard-reg-into-frame.rtl create mode 100644 gcc/testsuite/selftests/x86_64/times-two.rtl create mode 100644 gcc/testsuite/selftests/x86_64/unspec.rtl diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 9c46115f85c..1114d8a5f39 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,121 @@ +2017-01-05 David Malcolm + + * 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 * config/i386/i386.md (*testqi_ext_3): No need to handle memory diff --git a/gcc/Makefile.in b/gcc/Makefile.in index b9773f40c97..c53c78a2f03 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -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 \ diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index 9dd75b07498..4a3cf181622 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -64,6 +64,8 @@ #include "sched-int.h" #include "target-globals.h" #include "common/common-target.h" +#include "selftest.h" +#include "selftest-rtl.h" /* This file should be included last. */ #include "target-def.h" @@ -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" diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 9516f8ff582..3ad021a607f 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -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 (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 (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 diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index bfa42d2467f..0b9552b3541 100644 --- a/gcc/emit-rtl.c +++ b/gcc/emit-rtl.c @@ -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; +} + /* Return a value representing some low-order bits of X, where the number of low-order bits is given by MODE. Note that no conversion is done diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h index 4f4747bbc33..da60a2d808c 100644 --- a/gcc/emit-rtl.h +++ b/gcc/emit-rtl.h @@ -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 */ diff --git a/gcc/function-tests.c b/gcc/function-tests.c index b29735cdc74..ca30028143b 100644 --- a/gcc/function-tests.c +++ b/gcc/function-tests.c @@ -421,7 +421,7 @@ verify_three_block_gimple_cfg (function *fun) /* As above, but additionally verify the RTL insns are sane. */ -static void +void verify_three_block_rtl_cfg (function *fun) { verify_three_block_cfg (fun); diff --git a/gcc/function.c b/gcc/function.c index e0eb550fa3e..7fde96adbe3 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -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)) diff --git a/gcc/gensupport.c b/gcc/gensupport.c index 261e0022ad8..cef0a0c83c8 100644 --- a/gcc/gensupport.c +++ b/gcc/gensupport.c @@ -2233,7 +2233,7 @@ process_define_subst (void) class gen_reader : public rtx_reader { public: - gen_reader () : rtx_reader () {} + gen_reader () : rtx_reader (false) {} void handle_unknown_directive (file_location, const char *); }; diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c index 84d215b556d..30fd7597450 100644 --- a/gcc/print-rtl.c +++ b/gcc/print-rtl.c @@ -577,7 +577,7 @@ rtx_writer::print_rtx_operand (const_rtx in_rtx, int idx) string: if (str == 0) - fputs (" \"\"", m_outfile); + fputs (" (nil)", m_outfile); else fprintf (m_outfile, " (\"%s\")", str); m_sawclose = 1; diff --git a/gcc/read-md.c b/gcc/read-md.c index 7b73361830c..ac28944d671 100644 --- a/gcc/read-md.c +++ b/gcc/read-md.c @@ -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 . */ +/* 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. */ diff --git a/gcc/read-md.h b/gcc/read-md.h index a66a74af132..4fcbcb4b4e7 100644 --- a/gcc/read-md.h +++ b/gcc/read-md.h @@ -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 *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 m_reuse_rtx_by_id; }; /* Global singleton; constrast with md_reader_ptr above. */ diff --git a/gcc/read-rtl-function.c b/gcc/read-rtl-function.c new file mode 100644 index 00000000000..c5cb3f7953f --- /dev/null +++ b/gcc/read-rtl-function.c @@ -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 +. */ + +#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 {}; + hash_map m_insns_by_uid; + auto_vec m_fixups; + rtx_insn *m_first_insn; + auto_vec m_fake_scope; + char *m_name; + bool m_have_crtl_directive; + basic_block m_bb_to_insert_after; + auto_vec 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 (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 (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 (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 (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 "" 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, "")) + 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 index 00000000000..45ada84de14 --- /dev/null +++ b/gcc/read-rtl-function.h @@ -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 +. */ + +#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 */ diff --git a/gcc/read-rtl.c b/gcc/read-rtl.c index a08ce5c9f08..e9c80682800 100644 --- a/gcc/read-rtl.c +++ b/gcc/read-rtl.c @@ -17,7 +17,13 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ +/* 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 *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 + +#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... [ ... ]) @@ -1020,15 +1070,6 @@ check_code_iterator (struct mapping *iterator) bool rtx_reader::read_rtx (const char *rtx_name, vec *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 *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 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. */ diff --git a/gcc/rtl-tests.c b/gcc/rtl-tests.c index 044a6f17457..705434012d6 100644 --- a/gcc/rtl-tests.c +++ b/gcc/rtl-tests.c @@ -200,6 +200,7 @@ test_single_set () static void test_uncond_jump () { + set_new_first_and_last_insn (NULL, NULL); rtx_insn *label = gen_label_rtx (); rtx jump_pat = gen_rtx_SET (pc_rtx, gen_rtx_LABEL_REF (VOIDmode, diff --git a/gcc/rtl.h b/gcc/rtl.h index b9a7989c0d7..638dfc8b1cb 100644 --- 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 *); +#endif /* In alias.c */ extern rtx canon_rtx (rtx); diff --git a/gcc/selftest-rtl.c b/gcc/selftest-rtl.c new file mode 100644 index 00000000000..bfb1ab733a8 --- /dev/null +++ b/gcc/selftest-rtl.c @@ -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 +. */ + +#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 */ diff --git a/gcc/selftest-rtl.h b/gcc/selftest-rtl.h index 434dfa17183..2218f9c5280 100644 --- a/gcc/selftest-rtl.h +++ b/gcc/selftest-rtl.h @@ -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 */ diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c index 8bc6b0e5f26..f62bc72b072 100644 --- a/gcc/selftest-run-tests.c +++ b/gcc/selftest-run-tests.c @@ -72,6 +72,7 @@ selftest::run_tests () tree_c_tests (); gimple_c_tests (); rtl_tests_c_tests (); + read_rtl_function_c_tests (); /* Higher-level tests, or for components that other selftests don't rely on. */ diff --git a/gcc/selftest.h b/gcc/selftest.h index d13861c0426..dad53e9fe09 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -184,6 +184,7 @@ extern void hash_map_tests_c_tests (); extern void hash_set_tests_c_tests (); extern void input_c_tests (); extern void pretty_print_c_tests (); +extern void read_rtl_function_c_tests (); extern void rtl_tests_c_tests (); extern void selftest_c_tests (); extern void spellcheck_c_tests (); diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index aa6dc348cc3..7b11cf5b60e 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,27 @@ +2017-01-05 David Malcolm + + * 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 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 index 00000000000..05e2bdbf460 --- /dev/null +++ b/gcc/testsuite/selftests/aarch64/times-two.rtl @@ -0,0 +1,36 @@ +(function "times_two" + (insn-chain + (cnote 1 NOTE_INSN_DELETED) + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cnote 4 [bb 2] NOTE_INSN_BASIC_BLOCK) + (cinsn 2 (set (mem/c:SI (plus:DI (reg/f:DI virtual-stack-vars) + (const_int -4)) [1 i+0 S4 A32]) + (reg:SI x0 [ i ])) "../../src/times-two.c":2 + (nil)) + (cnote 3 NOTE_INSN_FUNCTION_BEG) + (cinsn 6 (set (reg:SI <2>) + (mem/c:SI (plus:DI (reg/f:DI virtual-stack-vars) + (const_int -4)) [1 i+0 S4 A32])) "../../src/times-two.c":3 + (nil)) + (cinsn 7 (set (reg:SI <0> [ _2 ]) + (ashift:SI (reg:SI <2>) + (const_int 1))) "../../src/times-two.c":3 + (nil)) + (cinsn 10 (set (reg:SI <1> [ ]) + (reg:SI <0> [ _2 ])) "../../src/times-two.c":3 + (nil)) + (cinsn 14 (set (reg/i:SI x0) + (reg:SI <1> [ ])) "../../src/times-two.c":4 + (nil)) + (cinsn 15 (use (reg/i:SI x0)) "../../src/times-two.c":4 + (nil)) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 2 + ) ;; insn-chain + (crtl + (return_rtx + (reg/i:SI x0) + ) ;; return_rtx + ) ;; crtl +) ;; function "times_two" diff --git a/gcc/testsuite/selftests/asr_div1.rtl b/gcc/testsuite/selftests/asr_div1.rtl new file mode 100644 index 00000000000..2b03b383622 --- /dev/null +++ b/gcc/testsuite/selftests/asr_div1.rtl @@ -0,0 +1,24 @@ +;; Taken from +;; gcc/testsuite/gcc.dg/asr_div1.c -O2 -fdump-rtl-all -mtune=cortex-a53 +;; for aarch64, hand editing to the new format. + +(function "f1" + (insn-chain + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cinsn 1 (set (reg:DI <2>) + (lshiftrt:DI (reg:DI <0>) + (const_int 32))) + "../../src/gcc/testsuite/gcc.dg/asr_div1.c":14 + (expr_list:REG_DEAD (reg:DI <0>) + (nil))) + (cinsn 2 (set (reg:SI <1>) + (ashiftrt:SI (subreg:SI (reg:DI <2>) 0) + (const_int 3))) + "../../src/gcc/testsuite/gcc.dg/asr_div1.c":14 + (expr_list:REG_DEAD (reg:DI <2>) + (nil))) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 2 + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/bb-index.rtl b/gcc/testsuite/selftests/bb-index.rtl new file mode 100644 index 00000000000..7c66f226b82 --- /dev/null +++ b/gcc/testsuite/selftests/bb-index.rtl @@ -0,0 +1,8 @@ +(function "test_bb_index" + (insn-chain + (block 42 + (edge-from entry (flags "FALLTHRU")) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 42 + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/cfg-test.rtl b/gcc/testsuite/selftests/cfg-test.rtl new file mode 100644 index 00000000000..08a0e226cae --- /dev/null +++ b/gcc/testsuite/selftests/cfg-test.rtl @@ -0,0 +1,37 @@ +/* Example of a loading a CFG like this: + 0 (entry) + | + 2 + / \ + 3 4 + \ / + 5 + | + 1 (exit). */ + +(function "cfg_test" + (insn-chain + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK) + (edge-to 3 (flags "TRUE_VALUE")) + (edge-to 4 (flags "FALSE_VALUE")) + ) ;; block 2 + (block 3 + (edge-from 2 (flags "TRUE_VALUE")) + (cnote 2 [bb 3] NOTE_INSN_BASIC_BLOCK) + (edge-to 5 (flags "FALLTHRU")) + ) ;; block 3 + (block 4 + (edge-from 2 (flags "FALSE_VALUE")) + (cnote 3 [bb 4] NOTE_INSN_BASIC_BLOCK) + (edge-to 5 (flags "FALLTHRU")) + ) ;; block 4 + (block 5 + (edge-from 3 (flags "FALLTHRU")) + (edge-from 4 (flags "FALLTHRU")) + (cnote 4 [bb 5] NOTE_INSN_BASIC_BLOCK) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 5 + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/const-int.rtl b/gcc/testsuite/selftests/const-int.rtl new file mode 100644 index 00000000000..477f156c663 --- /dev/null +++ b/gcc/testsuite/selftests/const-int.rtl @@ -0,0 +1,20 @@ +(function "const_int_examples" + (insn-chain + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cinsn 1 + (set (reg:SI <0>) (const_int 0)) + "test.c":2 (nil)) + (cinsn 2 + (set (reg:SI <1>) (const_int 1)) + "test.c":2 (nil)) + (cinsn 3 + (set (reg:SI <2>) (const_int -1)) + "test.c":2 (nil)) + (cinsn 4 + (set (reg:SI <3>) (const_int 256)) + "test.c":2 (nil)) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 2 + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/example-labels.rtl b/gcc/testsuite/selftests/example-labels.rtl new file mode 100644 index 00000000000..ec33bfd7e5a --- /dev/null +++ b/gcc/testsuite/selftests/example-labels.rtl @@ -0,0 +1,8 @@ +(function "example_labels" + (insn-chain + (block 6 + (clabel 100 30 (nil)) + (clabel 200 40 ("some_label_name")) + ) ;; block 6 + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/insn-with-mode.rtl b/gcc/testsuite/selftests/insn-with-mode.rtl new file mode 100644 index 00000000000..7e8f2a8803a --- /dev/null +++ b/gcc/testsuite/selftests/insn-with-mode.rtl @@ -0,0 +1,7 @@ +(function "insn_with_mode" + (insn-chain + (block 2 + (insn:TI 1 (set (reg:SI <0>) (reg:SI <1>)) (nil)) + ) ;; block + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/jump-to-label-ref.rtl b/gcc/testsuite/selftests/jump-to-label-ref.rtl new file mode 100644 index 00000000000..29184bfb4b9 --- /dev/null +++ b/gcc/testsuite/selftests/jump-to-label-ref.rtl @@ -0,0 +1,17 @@ +(function "jump_to_label_ref" + (insn-chain + (block 4 + (edge-from entry (flags "FALLTHRU")) + (cjump_insn 1 (set (pc) (label_ref 100)) + "../../src/gcc/testsuite/rtl.dg/test.c":4) + (edge-to 5) + ) ;; block 4 + (cbarrier 2) + (block 5 + (edge-from 4) + (clabel 100 2 (nil) [1 uses]) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 5 + ) ;; insn-chain +) ;; function + diff --git a/gcc/testsuite/selftests/jump-to-return.rtl b/gcc/testsuite/selftests/jump-to-return.rtl new file mode 100644 index 00000000000..9da89ef42df --- /dev/null +++ b/gcc/testsuite/selftests/jump-to-return.rtl @@ -0,0 +1,11 @@ +(function "jump_to_return" + (insn-chain + (block 4 + (edge-from entry (flags "FALLTHRU")) + (cjump_insn 1 (return) + "../../src/gcc/testsuite/rtl.dg/test.c":4 + (nil)) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 4 + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/jump-to-simple-return.rtl b/gcc/testsuite/selftests/jump-to-simple-return.rtl new file mode 100644 index 00000000000..5a9c1d57dbc --- /dev/null +++ b/gcc/testsuite/selftests/jump-to-simple-return.rtl @@ -0,0 +1,11 @@ +(function "jump_to_simple_return" + (insn-chain + (block 4 + (edge-from entry (flags "FALLTHRU")) + (cjump_insn 1 (simple_return) + "../../src/gcc/testsuite/rtl.dg/test.c":4 + (nil)) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 4 + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/mem.rtl b/gcc/testsuite/selftests/mem.rtl new file mode 100644 index 00000000000..219a4aa8072 --- /dev/null +++ b/gcc/testsuite/selftests/mem.rtl @@ -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 index 00000000000..a388acde396 --- /dev/null +++ b/gcc/testsuite/selftests/note-insn-deleted.rtl @@ -0,0 +1,5 @@ +(function "example_note" + (insn-chain + (cnote 1 NOTE_INSN_DELETED) + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/note_insn_basic_block.rtl b/gcc/testsuite/selftests/note_insn_basic_block.rtl new file mode 100644 index 00000000000..e792d983165 --- /dev/null +++ b/gcc/testsuite/selftests/note_insn_basic_block.rtl @@ -0,0 +1,9 @@ +(function "example_of_note" + (insn-chain + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cnote 1 [bb 2] NOTE_INSN_BASIC_BLOCK) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 2 + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/simple-cse.rtl b/gcc/testsuite/selftests/simple-cse.rtl new file mode 100644 index 00000000000..2dae4ae7433 --- /dev/null +++ b/gcc/testsuite/selftests/simple-cse.rtl @@ -0,0 +1,16 @@ +(function "test" + (insn-chain + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cinsn 1 (set (reg:SI <1>) + (plus:SI (reg:SI <0>) + (const_int 1))) (nil)) + (cinsn 2 (set (reg:SI <2>) + (plus:SI (reg:SI <0>) + (const_int 1))) (nil)) + (cinsn 3 (set (mem:SI (reg:SI <3>) [1 i+0 S4 A32]) + (mult:SI (reg:SI <1>) (reg:SI <2>))) (nil)) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 2 + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/symbol-ref.rtl b/gcc/testsuite/selftests/symbol-ref.rtl new file mode 100644 index 00000000000..98b583ef75d --- /dev/null +++ b/gcc/testsuite/selftests/symbol-ref.rtl @@ -0,0 +1,13 @@ +(function "example_of_symbol_ref" + (insn-chain + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cinsn 1 + (set (reg:SI <0>) + (high:SI (symbol_ref:SI ("isl_obj_map_vtable") [flags 0xc0] ))) + "y.c":12702 + (nil)) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 2 + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/x86_64/call-insn.rtl b/gcc/testsuite/selftests/x86_64/call-insn.rtl new file mode 100644 index 00000000000..8f3a7812687 --- /dev/null +++ b/gcc/testsuite/selftests/x86_64/call-insn.rtl @@ -0,0 +1,17 @@ +(function "test" + (insn-chain + (block 2 + (edge-from entry (flags "FALLTHRU")) + (ccall_insn/j 1 + (set (reg:DF xmm0) + (call (mem:QI (symbol_ref:DI ("sqrt") [flags 0x41] ) [0 __builtin_sqrt S1 A8]) + (const_int 0))) "test.c":19 + (expr_list:REG_CALL_DECL (symbol_ref:DI ("sqrt") [flags 0x41] ) + (expr_list:REG_EH_REGION (const_int 0) + (nil))) + (expr_list:DF (use (reg:DF xmm0)) + (nil))) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 2 + ) ;; insn-chain +) ;; function "test" diff --git a/gcc/testsuite/selftests/x86_64/copy-hard-reg-into-frame.rtl b/gcc/testsuite/selftests/x86_64/copy-hard-reg-into-frame.rtl new file mode 100644 index 00000000000..4598a1c9bfe --- /dev/null +++ b/gcc/testsuite/selftests/x86_64/copy-hard-reg-into-frame.rtl @@ -0,0 +1,15 @@ +(function "copy_hard_reg_into_frame" + (insn-chain + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cinsn 1 (set (mem/c:SI + (plus:DI + (reg/f:DI frame) + (const_int -4)) + [1 i+0 S4 A32]) + (reg:SI di [ i ])) "test.c":2 + (nil)) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 2 + ) ;; insn-chain +) ;; function diff --git a/gcc/testsuite/selftests/x86_64/times-two.rtl b/gcc/testsuite/selftests/x86_64/times-two.rtl new file mode 100644 index 00000000000..8cec47acd9a --- /dev/null +++ b/gcc/testsuite/selftests/x86_64/times-two.rtl @@ -0,0 +1,51 @@ +;; Dump of this C function: +;; +;; int times_two (int i) +;; { +;; return i * 2; +;; } +;; +;; after expand for target==x86_64 + +(function "times_two" + (insn-chain + (cnote 1 NOTE_INSN_DELETED) + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cnote 4 [bb 2] NOTE_INSN_BASIC_BLOCK) + (cinsn 2 (set (mem/c:SI (plus:DI (reg/f:DI virtual-stack-vars) + (const_int -4)) [1 i+0 S4 A32]) + (reg:SI di [ i ])) "../../src/times-two.c":2 + (nil)) + (cnote 3 NOTE_INSN_FUNCTION_BEG) + (cinsn 6 (set (reg:SI <2>) + (mem/c:SI (plus:DI (reg/f:DI virtual-stack-vars) + (const_int -4)) [1 i+0 S4 A32])) "../../src/times-two.c":3 + (nil)) + (cinsn 7 (parallel [ + (set (reg:SI <0> [ _2 ]) + (ashift:SI (reg:SI <2>) + (const_int 1))) + (clobber (reg:CC flags)) + ]) "../../src/times-two.c":3 + (expr_list:REG_EQUAL (ashift:SI (mem/c:SI (plus:DI (reg/f:DI virtual-stack-vars) + (const_int -4)) [1 i+0 S4 A32]) + (const_int 1)) + (nil))) + (cinsn 10 (set (reg:SI <1> [ ]) + (reg:SI <0> [ _2 ])) "../../src/times-two.c":3 + (nil)) + (cinsn 14 (set (reg/i:SI ax) + (reg:SI <1> [ ])) "../../src/times-two.c":4 + (nil)) + (cinsn 15 (use (reg/i:SI ax)) "../../src/times-two.c":4 + (nil)) + (edge-to exit (flags "FALLTHRU")) + ) ;; block 2 + ) ;; insn-chain + (crtl + (return_rtx + (reg/i:SI ax) + ) ;; return_rtx + ) ;; crtl +) ;; function "times_two" diff --git a/gcc/testsuite/selftests/x86_64/unspec.rtl b/gcc/testsuite/selftests/x86_64/unspec.rtl new file mode 100644 index 00000000000..ac822ac243e --- /dev/null +++ b/gcc/testsuite/selftests/x86_64/unspec.rtl @@ -0,0 +1,20 @@ +(function "test_unspec" + (insn-chain + (block 2 + (edge-from entry (flags "FALLTHRU")) + (cinsn 1 (set (mem/v:BLK (0|scratch:DI) [0 A8]) + (unspec:BLK [ + (mem/v:BLK (reuse_rtx 0) [0 A8]) + ] UNSPEC_MEMORY_BLOCKAGE)) "../../src/gcc/testsuite/gcc.dg/rtl/test.c":2 + (nil)) + + (cinsn 2 (set (mem/v:BLK (1|scratch:DI) [0 A8]) + (unspec_volatile:BLK [ + (mem/v:BLK (reuse_rtx 1) [0 A8]) + ] UNSPECV_RDTSCP)) "../../src/gcc/testsuite/gcc.dg/rtl/test.c":2 + (nil)) + + (edge-to exit (flags "FALLTHRU")) + ) ;; block 2 + ) ;; insn-chain +) ;; function diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c index 4f95682083b..4e47be1dc05 100644 --- a/gcc/tree-dfa.c +++ b/gcc/tree-dfa.c @@ -305,6 +305,11 @@ ssa_default_def (struct function *fn, tree var) gcc_assert (VAR_P (var) || TREE_CODE (var) == PARM_DECL || TREE_CODE (var) == RESULT_DECL); + + /* Always NULL_TREE for rtl function dumps. */ + if (!fn->gimple_df) + return NULL_TREE; + in.var = (tree)&ind; ind.uid = DECL_UID (var); return DEFAULT_DEFS (fn)->find_with_hash ((tree)&in, DECL_UID (var)); -- 2.30.2