From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 105425 invoked by alias); 9 Sep 2016 00:01:55 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Received: (qmail 105316 invoked by uid 89); 9 Sep 2016 00:01:54 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=3.3 required=5.0 tests=BAYES_50,KAM_LAZY_DOMAIN_SECURITY,KAM_STOCKGEN autolearn=no version=3.3.2 spammy=8888, relating, 2128, RETURN X-HELO: eggs.gnu.org Received: from eggs.gnu.org (HELO eggs.gnu.org) (208.118.235.92) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Fri, 09 Sep 2016 00:01:31 +0000 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1bi9Fw-0001re-3w for gcc-patches@gcc.gnu.org; Thu, 08 Sep 2016 20:01:29 -0400 Received: from mx1.redhat.com ([209.132.183.28]:46578) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1bi9Fv-0001rA-Gz for gcc-patches@gcc.gnu.org; Thu, 08 Sep 2016 20:01:20 -0400 Received: from int-mx14.intmail.prod.int.phx2.redhat.com (int-mx14.intmail.prod.int.phx2.redhat.com [10.5.11.27]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 3FFFAC037F65 for ; Fri, 9 Sep 2016 00:01:17 +0000 (UTC) Received: from c64.redhat.com (vpn-232-118.phx2.redhat.com [10.3.232.118]) by int-mx14.intmail.prod.int.phx2.redhat.com (8.14.4/8.14.4) with ESMTP id u8901Di9023199; Thu, 8 Sep 2016 20:01:16 -0400 From: David Malcolm To: gcc-patches@gcc.gnu.org Cc: David Malcolm Subject: [PATCH 5/9] Introduce class function_reader Date: Fri, 09 Sep 2016 00:13:00 -0000 Message-Id: <1473381053-18817-6-git-send-email-dmalcolm@redhat.com> In-Reply-To: <1473381053-18817-1-git-send-email-dmalcolm@redhat.com> References: <1473381053-18817-1-git-send-email-dmalcolm@redhat.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.2.x-3.x [generic] X-Received-From: 209.132.183.28 X-IsSubscribed: yes X-SW-Source: 2016-09/txt/msg00486.txt.bz2 This patch generalizes the RTL-reading capabilities so that they can be run on the host as well as the build machine. The available rtx in rtl.def changes dramatically between these two configurations, so a fair amount of #ifdef GENERATOR_FILE is required to express this. This patch introduces a function_reader subclass of rtx_reader, capable of reading an RTL function dump (or part of one), reconstructing a cfun with a CFG and basic blocks containing insns. gcc/ChangeLog: * Makefile.in (OBJS): Add errors.o, read-md.o, read-rtl.o, read-rtl-function.o, and selftest-rtl.o. * cfgexpand.c (pass_expand::execute): Move stack initializations to rtl_data::init_stack_alignment and call it. Pass "true" for new "emit_insns" param of expand_function_start. * emit-rtl.c (gen_reg_rtx): Move regno_pointer_align and regno_reg_rtx resizing logic to... (emit_status::ensure_regno_capacity): ...this new method. (init_emit): Allocate regno_reg_rtx using ggc_cleared_vec_alloc rather than ggc_vec_alloc. (rtl_data::init_stack_alignment): New method. (get_insn_by_uid): New function. * emit-rtl.h (rtl_data::init_stack_alignment): New method. * errors.c: Use consistent pattern for bconfig.h vs config.h includes. (progname): Wrap with #ifdef GENERATOR_FILE. (error): Likewise. Add "error: " to message. (fatal): Likewise. (internal_error): Likewise. (trim_filename): Likewise. (fancy_abort): Likewise. * errors.h (struct file_location): Move here from read-md.h. (file_location::file_location): Likewise. (error_at): New decl. * function-tests.c (selftest::verify_three_block_rtl_cfg): Remove "static". * function.c (instantiate_decls): Guard call to instantiate_decls_1 with if (DECL_INITIAL (fndecl)). (expand_function_start): Add param "emit_insns", and use it to guard the various gen/emit calls. * function.h (emit_status::ensure_regno_capacity): New method. (expand_function_start): Add bool param to decl. * gensupport.c (gen_reader::gen_reader): Add NULL for new policy param of rtx_reader ctor. * print-rtl.c (print_rtx): Print "(nil)" rather than an empty string for NULL strings. Print "(nil)" for NULL basic blocks. * read-md.c (read_skip_construct): Provide forward decl. (read_skip_spaces): Support '/'. (require_char): New function. (require_word_ws): New function. (peek_char): New function. (read_name): Rename to... (read_name_1): ...this new static function, adding "out_loc" param, and converting "missing name or number" to returning false, rather than failing. (read_name): Reimplement in terms of read_name_1. (read_name_or_nil): New function. (read_string): Handle "(nil)" by returning NULL. */ (rtx_reader::rtx_reader): Add rtl_reader_policy * param, using it to initialize m_policy. (rtx_reader::~rtx_reader): Free m_base_dir. Clean up global data. * read-md.h (struct file_location): Move to errors.h. (file_location::file_location): Likewise. Include errors.h. (class regno_remapper): New class. (struct rtl_reader_policy): New struct. (rtx_reader::rtx_reader): Add rtl_reader_policy * param. (rtx_reader::add_fixup_insn_uid): New vfunc. (rtx_reader::add_fixup_bb): New vfunc. (rtx_reader::add_fixup_note_insn_basic_block): New vfunc. (rtx_reader::add_fixup_source_location): New vfunc. (rtx_reader::add_fixup_jump_label): New vfunc. (rtx_reader::add_fixup_expr): New vfunc. (rtx_reader::remap_regno): New method. (rtx_reader::m_policy): New field. (noop_reader::noop_reader): Add NULL for new policy param of rtx_reader ctor. (peek_char): New decl. (require_char): New decl. (require_word_ws): New decl. (read_name): Convert return type from void to file_location. (read_name_or_nil): New decl. * read-rtl-function.c: New file. * read-rtl-function.h: New file. * read-rtl.c: Potentially include config.h rather than bconfig.h. For host, include function.h and emit-rtl.h. (apply_subst_iterator): Wrap with #ifdef GENERATOR_FILE. (bind_subst_iter_and_attr): Likewise. (add_condition_to_string): Likewise. (add_condition_to_rtx): Likewise. (apply_attribute_uses): Likewise. (add_current_iterators): Likewise. (apply_iterators): Likewise. (initialize_iterators): Guard usage of apply_subst_iterator with #ifdef GENERATOR_FILE. (read_conditions): Wrap with #ifdef GENERATOR_FILE. (read_mapping): Likewise. (add_define_attr_for_define_subst): Likewise. (add_define_subst_attr): Likewise. (read_subst_mapping): Likewise. (check_code_iterator): Likewise. (read_rtx): Likewise. Move one-time initialization logic to... (one_time_initialization): New function. (parse_reg_note_name): New function. (parse_note_insn_name): New function. (maybe_read_location): New function. (read_until): New function. (strip_trailing_whitespace): New function. (read_flags): New function. (regno_remapper::get_effective_regno): New method. (rtx_reader::remap_regno): New method. (read_rtx_code): Remove "static". Initialize "iterator" to NULL. Call one_time_initialization. Wrap iterator lookup within #ifdef GENERATOR_FILE. Add parsing support for RTL dumps, mirroring the special-cases in print_rtx, by calling read_flags, reading SYMBOL_REF data, REG_NOTE names, INSN_UID values, note-specific data, basic block IDs, jump labels, hexdump values of wide ints, REG_EXPR, MEM_EXPR, skipping recognized insn names, remapping register numbers. When on host, reallocate XSTR and XTMPL fields in the GC-managed heap. (lookup_global_register): New function. (consolidate_reg): New function. (consolidate_singletons): New function. (read_nested_rtx): Call consolidate_singletons. * rtl.h (read_rtx): Wrap decl with #ifdef GENERATOR_FILE. (read_rtx_code): New decl. * selftest-rtl.c: New file. * selftest-rtl.h: New file. * selftest-run-tests.c (selftest::run_tests): Run read_rtl_function_c_tests. * selftest.h (selftest::read_rtl_function_c_tests): New decl. * tree-dfa.c (ssa_default_def): Return NULL_TREE for rtl function dumps. --- gcc/Makefile.in | 5 + gcc/cfgexpand.c | 7 +- gcc/emit-rtl.c | 70 ++- gcc/emit-rtl.h | 2 + gcc/errors.c | 23 +- gcc/errors.h | 13 + gcc/function-tests.c | 2 +- gcc/function.c | 41 +- gcc/function.h | 4 +- gcc/gensupport.c | 2 +- gcc/print-rtl.c | 4 +- gcc/read-md.c | 109 ++++- gcc/read-md.h | 100 +++- gcc/read-rtl-function.c | 1197 ++++++++++++++++++++++++++++++++++++++++++++++ gcc/read-rtl-function.h | 29 ++ gcc/read-rtl.c | 757 ++++++++++++++++++++++++++++- gcc/rtl.h | 4 + gcc/selftest-rtl.c | 81 ++++ gcc/selftest-rtl.h | 66 +++ gcc/selftest-run-tests.c | 1 + gcc/selftest.h | 1 + gcc/tree-dfa.c | 5 + 22 files changed, 2434 insertions(+), 89 deletions(-) create mode 100644 gcc/read-rtl-function.c create mode 100644 gcc/read-rtl-function.h create mode 100644 gcc/selftest-rtl.c create mode 100644 gcc/selftest-rtl.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 7c18285..341c188 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1265,6 +1265,7 @@ OBJS = \ dwarf2cfi.o \ dwarf2out.o \ emit-rtl.o \ + errors.o \ et-forest.o \ except.o \ explow.o \ @@ -1401,6 +1402,9 @@ OBJS = \ print-rtl.o \ print-tree.o \ profile.o \ + read-md.o \ + read-rtl.o \ + read-rtl-function.o \ real.o \ realmpfr.o \ recog.o \ @@ -1428,6 +1432,7 @@ OBJS = \ sel-sched-ir.o \ sel-sched-dump.o \ sel-sched.o \ + selftest-rtl.o \ selftest-run-tests.o \ sese.o \ shrink-wrap.o \ diff --git a/gcc/cfgexpand.c b/gcc/cfgexpand.c index 130a16b..f97e1fd 100644 --- a/gcc/cfgexpand.c +++ b/gcc/cfgexpand.c @@ -6219,10 +6219,7 @@ pass_expand::execute (function *fun) discover_nonconstant_array_refs (); targetm.expand_to_rtl_hook (); - crtl->stack_alignment_needed = STACK_BOUNDARY; - crtl->max_used_stack_slot_alignment = STACK_BOUNDARY; - crtl->stack_alignment_estimated = 0; - crtl->preferred_stack_boundary = STACK_BOUNDARY; + crtl->init_stack_alignment (); fun->cfg->max_jumptable_ents = 0; /* Resovle the function section. Some targets, like ARM EABI rely on knowledge @@ -6254,7 +6251,7 @@ pass_expand::execute (function *fun) } /* Set up parameters and prepare for return, for the function. */ - expand_function_start (current_function_decl); + expand_function_start (current_function_decl, true); /* If we emitted any instructions for setting up the variables, emit them before the FUNCTION_START note. */ diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c index a724608..4fdf708 100644 --- a/gcc/emit-rtl.c +++ b/gcc/emit-rtl.c @@ -1056,29 +1056,35 @@ gen_reg_rtx (machine_mode mode) /* Do not call gen_reg_rtx with uninitialized crtl. */ gcc_assert (crtl->emit.regno_pointer_align_length); - /* Make sure regno_pointer_align, and regno_reg_rtx are large - enough to have an element for this pseudo reg number. */ + int cur_size = crtl->emit.regno_pointer_align_length; + if (reg_rtx_no == cur_size) + crtl->emit.ensure_regno_capacity (cur_size * 2); - if (reg_rtx_no == crtl->emit.regno_pointer_align_length) - { - int old_size = crtl->emit.regno_pointer_align_length; - char *tmp; - rtx *new1; + val = gen_raw_REG (mode, reg_rtx_no); + regno_reg_rtx[reg_rtx_no++] = val; + return val; +} - tmp = XRESIZEVEC (char, crtl->emit.regno_pointer_align, old_size * 2); - memset (tmp + old_size, 0, old_size); - crtl->emit.regno_pointer_align = (unsigned char *) tmp; +/* Make sure m_regno_pointer_align, and regno_reg_rtx are large + enough to have elements in the range 0 <= idx < NEW_SIZE. */ - new1 = GGC_RESIZEVEC (rtx, regno_reg_rtx, old_size * 2); - memset (new1 + old_size, 0, old_size * sizeof (rtx)); - regno_reg_rtx = new1; +void +emit_status::ensure_regno_capacity (int new_size) +{ + if (new_size < regno_pointer_align_length) + return; - crtl->emit.regno_pointer_align_length = old_size * 2; - } + int old_size = regno_pointer_align_length; - val = gen_raw_REG (mode, reg_rtx_no); - regno_reg_rtx[reg_rtx_no++] = val; - return val; + char *tmp = XRESIZEVEC (char, regno_pointer_align, new_size); + memset (tmp + old_size, 0, new_size - old_size); + regno_pointer_align = (unsigned char *) tmp; + + rtx *new1 = GGC_RESIZEVEC (rtx, regno_reg_rtx, new_size); + memset (new1 + old_size, 0, (new_size - old_size) * sizeof (rtx)); + regno_reg_rtx = new1; + + crtl->emit.regno_pointer_align_length = new_size; } /* Return TRUE if REG is a PARM_DECL, FALSE otherwise. */ @@ -5686,7 +5692,8 @@ init_emit (void) crtl->emit.regno_pointer_align = XCNEWVEC (unsigned char, crtl->emit.regno_pointer_align_length); - regno_reg_rtx = ggc_vec_alloc (crtl->emit.regno_pointer_align_length); + regno_reg_rtx = + ggc_cleared_vec_alloc (crtl->emit.regno_pointer_align_length); /* Put copies of all the hard registers into regno_reg_rtx. */ memcpy (regno_reg_rtx, @@ -6304,5 +6311,30 @@ need_atomic_barrier_p (enum memmodel model, bool pre) gcc_unreachable (); } } + +/* Initialize fields of rtl_data related to stack alignment. */ + +void +rtl_data::init_stack_alignment () +{ + stack_alignment_needed = STACK_BOUNDARY; + max_used_stack_slot_alignment = STACK_BOUNDARY; + stack_alignment_estimated = 0; + preferred_stack_boundary = STACK_BOUNDARY; +} + +/* Get the insn with the given uid, or NULL if not found. */ + +rtx_insn * +get_insn_by_uid (int uid) +{ + for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn)) + if (INSN_UID (insn) == uid) + return insn; + + /* Not found. */ + return NULL; +} + #include "gt-emit-rtl.h" diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h index 52c72b1..663b4a0 100644 --- a/gcc/emit-rtl.h +++ b/gcc/emit-rtl.h @@ -55,6 +55,8 @@ struct GTY(()) incoming_args { /* Datastructures maintained for currently processed function in RTL form. */ struct GTY(()) rtl_data { + void init_stack_alignment (); + struct expr_status expr; struct emit_status emit; struct varasm_status varasm; diff --git a/gcc/errors.c b/gcc/errors.c index 468384c..8df94ab 100644 --- a/gcc/errors.c +++ b/gcc/errors.c @@ -21,18 +21,21 @@ along with GCC; see the file COPYING3. If not see in the generator programs; the compiler has a more elaborate suite of diagnostic printers, found in diagnostic.c. */ -#ifdef HOST_GENERATOR_FILE -#include "config.h" -#define GENERATOR_FILE 1 -#else +/* This file is compiled twice: once for the generator programs + once for the compiler. */ +#ifdef GENERATOR_FILE #include "bconfig.h" +#else +#include "config.h" #endif #include "system.h" #include "errors.h" /* Set this to argv[0] at the beginning of main. */ +#ifdef GENERATOR_FILE const char *progname; +#endif /* #ifdef GENERATOR_FILE */ /* Starts out 0, set to 1 if error is called. */ @@ -55,13 +58,15 @@ warning (const char *format, ...) /* Print an error message - we keep going but the output is unusable. */ +#ifdef GENERATOR_FILE + void error (const char *format, ...) { va_list ap; va_start (ap, format); - fprintf (stderr, "%s: ", progname); + fprintf (stderr, "%s: error: ", progname); vfprintf (stderr, format, ap); va_end (ap); fputc ('\n', stderr); @@ -69,6 +74,7 @@ error (const char *format, ...) have_error = 1; } +#endif /* #ifdef GENERATOR_FILE */ /* Fatal error - terminate execution immediately. Does not return. */ @@ -78,7 +84,7 @@ fatal (const char *format, ...) va_list ap; va_start (ap, format); - fprintf (stderr, "%s: ", progname); + fprintf (stderr, "%s: error: ", progname); vfprintf (stderr, format, ap); va_end (ap); fputc ('\n', stderr); @@ -87,6 +93,8 @@ fatal (const char *format, ...) /* Similar, but say we got an internal error. */ +#ifdef GENERATOR_FILE + void internal_error (const char *format, ...) { @@ -127,8 +135,11 @@ trim_filename (const char *name) /* "Fancy" abort. Reports where in the compiler someone gave up. This file is used only by build programs, so we're not as polite as the version in diagnostic.c. */ + void fancy_abort (const char *file, int line, const char *func) { internal_error ("abort in %s, at %s:%d", func, trim_filename (file), line); } + +#endif /* #ifdef GENERATOR_FILE */ diff --git a/gcc/errors.h b/gcc/errors.h index a6973f3..c63f06b 100644 --- a/gcc/errors.h +++ b/gcc/errors.h @@ -28,8 +28,21 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_ERRORS_H #define GCC_ERRORS_H +/* Records a position in the file. */ +struct file_location { + file_location () {} + file_location (const char *, int); + + const char *filename; + int lineno; +}; + +inline file_location::file_location (const char *filename_in, int lineno_in) + : filename (filename_in), lineno (lineno_in) {} + extern void warning (const char *, ...) ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD; extern void error (const char *, ...) ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD; +extern void error_at (file_location, const char *, ...) ATTRIBUTE_PRINTF_2 ATTRIBUTE_COLD; extern void fatal (const char *, ...) ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD; extern void internal_error (const char *, ...) ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF_1 ATTRIBUTE_COLD; extern const char *trim_filename (const char *); diff --git a/gcc/function-tests.c b/gcc/function-tests.c index 4152cd3..2235b4c 100644 --- a/gcc/function-tests.c +++ b/gcc/function-tests.c @@ -420,7 +420,7 @@ verify_three_block_gimple_cfg (function *fun) /* As above, but additionally verify the RTL insns are sane. */ -static void +void verify_three_block_rtl_cfg (function *fun) { verify_three_block_cfg (fun); diff --git a/gcc/function.c b/gcc/function.c index 53bad87..fe9a07d 100644 --- a/gcc/function.c +++ b/gcc/function.c @@ -1901,7 +1901,8 @@ instantiate_decls (tree fndecl) instantiate_decl_rtl (DECL_RTL (DECL_VALUE_EXPR (decl))); /* Now process all variables defined in the function or its subblocks. */ - instantiate_decls_1 (DECL_INITIAL (fndecl)); + if (DECL_INITIAL (fndecl)) + instantiate_decls_1 (DECL_INITIAL (fndecl)); FOR_EACH_LOCAL_DECL (cfun, ix, decl) if (DECL_RTL_SET_P (decl)) @@ -5087,10 +5088,12 @@ stack_protect_epilogue (void) emitting RTL. SUBR is the FUNCTION_DECL node. PARMS_HAVE_CLEANUPS is nonzero if there are cleanups associated with - the function's parameters, which must be run at any return statement. */ + the function's parameters, which must be run at any return statement. + EMIT_INSNS is true if instructions are to emitted, false to avoid this + (for use when loading RTL function dumps). */ void -expand_function_start (tree subr) +expand_function_start (tree subr, bool emit_insns) { /* Make sure volatile mem refs aren't considered valid operands of arithmetic insns. */ @@ -5106,7 +5109,8 @@ expand_function_start (tree subr) /* Make the label for return statements to jump to. Do not special case machines with special return instructions -- they will be handled later during jump, ifcvt, or epilogue creation. */ - return_label = gen_label_rtx (); + if (emit_insns) + return_label = gen_label_rtx (); /* Initialize rtx used to return the value. */ /* Do this before assign_parms so that we copy the struct value address @@ -5132,7 +5136,7 @@ expand_function_start (tree subr) /* Expect to be passed the address of a place to store the value. If it is passed as an argument, assign_parms will take care of it. */ - if (sv) + if (sv && emit_insns) { value_address = gen_reg_rtx (Pmode); emit_move_insn (value_address, sv); @@ -5209,7 +5213,7 @@ expand_function_start (tree subr) assign_parms (subr); /* If function gets a static chain arg, store it. */ - if (cfun->static_chain_decl) + if (cfun->static_chain_decl && emit_insns) { tree parm = cfun->static_chain_decl; rtx local, chain; @@ -5253,7 +5257,7 @@ expand_function_start (tree subr) /* If the function receives a non-local goto, then store the bits we need to restore the frame pointer. */ - if (cfun->nonlocal_goto_save_area) + if (cfun->nonlocal_goto_save_area && emit_insns) { tree t_save; rtx r_save; @@ -5276,22 +5280,25 @@ expand_function_start (tree subr) The move is supposed to make sdb output more accurate. */ /* Indicate the beginning of the function body, as opposed to parm setup. */ - emit_note (NOTE_INSN_FUNCTION_BEG); + if (emit_insns) + { + emit_note (NOTE_INSN_FUNCTION_BEG); - gcc_assert (NOTE_P (get_last_insn ())); + gcc_assert (NOTE_P (get_last_insn ())); - parm_birth_insn = get_last_insn (); + parm_birth_insn = get_last_insn (); - if (crtl->profile) - { + if (crtl->profile) + { #ifdef PROFILE_HOOK - PROFILE_HOOK (current_function_funcdef_no); + PROFILE_HOOK (current_function_funcdef_no); #endif - } + } - /* If we are doing generic stack checking, the probe should go here. */ - if (flag_stack_check == GENERIC_STACK_CHECK) - stack_check_probe_note = emit_note (NOTE_INSN_DELETED); + /* If we are doing generic stack checking, the probe should go here. */ + if (flag_stack_check == GENERIC_STACK_CHECK) + stack_check_probe_note = emit_note (NOTE_INSN_DELETED); + } } void diff --git a/gcc/function.h b/gcc/function.h index 590a490..40aa910 100644 --- a/gcc/function.h +++ b/gcc/function.h @@ -34,6 +34,8 @@ struct GTY(()) sequence_stack { }; struct GTY(()) emit_status { + void ensure_regno_capacity (int new_size); + /* This is reset to LAST_VIRTUAL_REGISTER + 1 at the start of each function. After rtl generation, it is 1 plus the largest register number used. */ int x_reg_rtx_no; @@ -619,7 +621,7 @@ extern void pop_dummy_function (void); extern void init_dummy_function_start (void); extern void init_function_start (tree); extern void stack_protect_epilogue (void); -extern void expand_function_start (tree); +extern void expand_function_start (tree, bool); extern void expand_dummy_function_end (void); extern void thread_prologue_and_epilogue_insns (void); diff --git a/gcc/gensupport.c b/gcc/gensupport.c index 3abb259b..378e0bf 100644 --- a/gcc/gensupport.c +++ b/gcc/gensupport.c @@ -2234,7 +2234,7 @@ process_define_subst (void) class gen_reader : public rtx_reader { public: - gen_reader () : rtx_reader () {} + gen_reader () : rtx_reader (NULL) {} void handle_unknown_directive (file_location, const char *); }; diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c index a905127..a2cb69a 100644 --- a/gcc/print-rtl.c +++ b/gcc/print-rtl.c @@ -220,7 +220,7 @@ print_rtx (const_rtx in_rtx) string: if (str == 0) - fputs (" \"\"", outfile); + fputs (" (nil)", outfile); else fprintf (outfile, " (\"%s\")", str); sawclose = 1; @@ -586,6 +586,8 @@ print_rtx (const_rtx in_rtx) #ifndef GENERATOR_FILE if (XBBDEF (in_rtx, i)) fprintf (outfile, " %i", XBBDEF (in_rtx, i)->index); + else + fprintf (outfile, " (nil)"); #endif break; diff --git a/gcc/read-md.c b/gcc/read-md.c index 77e558c..217ff49 100644 --- a/gcc/read-md.c +++ b/gcc/read-md.c @@ -65,6 +65,8 @@ static htab_t md_constants; /* A table of enum_type structures, hashed by name. */ static htab_t enum_types; +static void read_skip_construct (int depth, file_location loc); + /* Given an object that starts with a char * name field, return a hash code for its name. */ @@ -345,7 +347,7 @@ read_skip_spaces (void) if (c != '*') { unread_char (c); - fatal_with_file_and_line ("stray '/' in file"); + return '/'; } prevc = 0; @@ -364,6 +366,16 @@ read_skip_spaces (void) } } +/* Consume the next character, issuing a fatal error if it is not + EXPECTED. */ + +void require_char (char expected) +{ + int ch = read_char (); + if (ch != expected) + fatal_expected_char (expected, ch); +} + /* Consume any whitespace, then consume the next non-whitespace character, issuing a fatal error if it is not EXPECTED. */ @@ -375,6 +387,17 @@ require_char_ws (char expected) fatal_expected_char (expected, ch); } +/* Consume any whitespace, then consume the next word (as per read_name), + issuing a fatal error if it is not EXPECTED. */ + +void require_word_ws (const char *expected) +{ + struct md_name name; + read_name (&name); + if (strcmp (name.string, expected)) + fatal_with_file_and_line ("missing '%s'", expected); +} + /* Read the next character from the file. */ int @@ -399,11 +422,21 @@ rtx_reader::unread_char (int ch) ungetc (ch, m_read_md_file); } +/* Peek at the next character from the file without consuming it. */ + +int +peek_char (void) +{ + int ch = read_char (); + unread_char (ch); + return ch; +} + /* Read an rtx code name into NAME. It is terminated by any of the punctuation chars of rtx printed syntax. */ -void -read_name (struct md_name *name) +static bool +read_name_1 (struct md_name *name, file_location *out_loc) { int c; size_t i; @@ -441,8 +474,12 @@ read_name (struct md_name *name) c = read_char (); } + unread_char (c); + *out_loc = rtx_reader_ptr->get_current_location (); + read_char (); + if (i == 0) - fatal_with_file_and_line ("missing name or number"); + return false; name->buffer[i] = 0; name->string = name->buffer; @@ -463,6 +500,36 @@ read_name (struct md_name *name) } while (def); } + + return true; +} + +/* Read an rtx code name into NAME. It is terminated by any of the + punctuation chars of rtx printed syntax. */ + +file_location +read_name (struct md_name *name) +{ + file_location loc; + if (!read_name_1 (name, &loc)) + fatal_with_file_and_line ("missing name or number"); + return loc; +} + +file_location +read_name_or_nil (struct md_name *name) +{ + file_location loc; + if (!read_name_1 (name, &loc)) + { + file_location loc = rtx_reader_ptr->get_current_location (); + read_skip_construct (0, loc); + /* Skip the ')'. */ + read_char (); + name->buffer[0] = 0; + name->string = name->buffer; + } + return loc; } /* Subroutine of the string readers. Handles backslash escapes. @@ -608,6 +675,14 @@ read_string (int star_if_braced) obstack_1grow (&string_obstack, '*'); stringbuf = read_braced_string (); } + else if (saw_paren && c == 'n') + { + /* Handle (nil) by returning NULL. */ + require_char ('i'); + require_char ('l'); + require_char_ws (')'); + return NULL; + } else fatal_with_file_and_line ("expected `\"' or `{', found `%c'", c); @@ -902,12 +977,13 @@ traverse_enum_types (htab_trav callback, void *info) /* Constructor for rtx_reader. */ -rtx_reader::rtx_reader () +rtx_reader::rtx_reader (rtl_reader_policy *policy) : m_toplevel_fname (NULL), m_read_md_filename (NULL), m_read_md_lineno (0), m_first_dir_md_include (NULL), - m_last_dir_md_include_ptr (&m_first_dir_md_include) + m_last_dir_md_include_ptr (&m_first_dir_md_include), + m_policy (policy) { /* Set the global singleton pointer. */ rtx_reader_ptr = this; @@ -917,6 +993,27 @@ rtx_reader::rtx_reader () rtx_reader::~rtx_reader () { + free (m_base_dir); + + /* Clean up global data. */ + htab_delete (enum_types); + enum_types = NULL; + + htab_delete (md_constants); + md_constants = NULL; + + obstack_free (&joined_conditions_obstack, NULL); + + htab_delete (joined_conditions); + joined_conditions = NULL; + + obstack_free (&ptr_loc_obstack, NULL); + + htab_delete (ptr_locs); + ptr_locs = NULL; + + obstack_free (&string_obstack, NULL); + /* Clear the global singleton pointer. */ rtx_reader_ptr = NULL; } diff --git a/gcc/read-md.h b/gcc/read-md.h index 82a628b..33d33c4 100644 --- a/gcc/read-md.h +++ b/gcc/read-md.h @@ -21,18 +21,7 @@ along with GCC; see the file COPYING3. If not see #define GCC_READ_MD_H #include "obstack.h" - -/* Records a position in the file. */ -struct file_location { - file_location () {} - file_location (const char *, int); - - const char *filename; - int lineno; -}; - -inline file_location::file_location (const char *filename_in, int lineno_in) - : filename (filename_in), lineno (lineno_in) {} +#include "errors.h" /* Holds one symbol or number in the .md file. */ struct md_name { @@ -90,10 +79,41 @@ struct enum_type { unsigned int num_values; }; +/* A class for remapping register numbers in dumpfiles to + equivalent register numbers for the current target. */ + +class regno_remapper +{ + public: + /* Zero means "do nothing", otherwise DUMPED_FIRST_PSEUDO_REGNO + is the lowest numbered pseudo-reg seen in the dumpfile. */ + regno_remapper (int dumped_first_pseudo_regno) + : m_dumped_first_pseudo_regno (dumped_first_pseudo_regno) {} + + unsigned int get_effective_regno (int dumped_regno) const; + + private: + int m_dumped_first_pseudo_regno; +}; + +/* An optional policy class for class rtx_reader. */ + +struct rtl_reader_policy +{ + rtl_reader_policy (regno_remapper *regno_remapper_) + : m_regno_remapper (regno_remapper_) + { + } + + regno_remapper *m_regno_remapper; +}; + +/* A class for reading .md files and RTL dump files. */ + class rtx_reader { public: - rtx_reader (); + rtx_reader (rtl_reader_policy *policy); virtual ~rtx_reader (); bool read_md_files (int, const char **, bool (*) (const char *)); @@ -104,6 +124,47 @@ class rtx_reader unread character is the optional space after the directive name. */ virtual void handle_unknown_directive (file_location, const char *) = 0; + /* A hook for the RTL function dump reader to override, to record insn UIDs + for later fixup. */ + virtual void add_fixup_insn_uid (file_location /*loc*/, rtx /*insn*/, + int /*operand_idx*/, int /*insn_uid*/) + {} + + /* A hook for the RTL function dump reader to override, to record + basic block IDs for later fixup. */ + virtual void add_fixup_bb (file_location /*loc*/, rtx /*insn*/, + int /*operand_idx*/, int /*bb_idx*/) + {} + + /* A hook for the RTL function dump reader to override, to record + basic block IDs for fixup of NOTE_INSN_BASIC_BLOCK. */ + virtual void add_fixup_note_insn_basic_block (file_location /*loc*/, + rtx /*insn*/, + int /*operand_idx*/, + int /*bb_idx*/) + {} + + /* A hook for the RTL function dump reader to override, to record + source location information for fixing up into location_t values. */ + virtual void add_fixup_source_location (file_location /*loc*/, rtx /*insn*/, + int /*operand_idx*/, + const char */*filename*/, + int /*lineno*/) + {} + + /* A hook for the RTL function dump reader to override, to record + label names for fixing up the JUMP_LABEL of an insn. */ + virtual void add_fixup_jump_label (file_location /*loc*/, rtx /*insn*/, + int /*operand_idx*/, + const char */*label*/) + {} + + /* A hook for the RTL function dump reader to override, to record + textual tree dumps for fix up the expr of an rtx (REG or MEM). */ + virtual void add_fixup_expr (file_location /*loc*/, rtx /*x*/, + const char */*desc*/) + {} + file_location get_current_location () const; int read_char (void); @@ -113,6 +174,8 @@ class rtx_reader const char *get_filename () const { return m_read_md_filename; } int get_lineno () const { return m_read_md_lineno; } + int remap_regno (int dumped_regno) const; + private: /* A singly-linked list of filenames. */ struct file_name_list { @@ -149,6 +212,8 @@ class rtx_reader /* A pointer to the null terminator of the md include chain. */ file_name_list **m_last_dir_md_include_ptr; + + rtl_reader_policy *m_policy; }; /* Global singleton. */ @@ -159,7 +224,7 @@ extern rtx_reader *rtx_reader_ptr; class noop_reader : public rtx_reader { public: - noop_reader () : rtx_reader () {} + noop_reader () : rtx_reader (NULL) {} /* A dummy implementation which skips unknown directives. */ void handle_unknown_directive (file_location, const char *); @@ -184,6 +249,8 @@ unread_char (int ch) rtx_reader_ptr->unread_char (ch); } +extern int peek_char (void); + extern hashval_t leading_string_hash (const void *); extern int leading_string_eq_p (const void *, const void *); extern void copy_md_ptr_loc (const void *, const void *); @@ -199,8 +266,11 @@ extern void fatal_with_file_and_line (const char *, ...) ATTRIBUTE_PRINTF_1 ATTRIBUTE_NORETURN; extern void fatal_expected_char (int, int) ATTRIBUTE_NORETURN; extern int read_skip_spaces (void); +extern void require_char (char expected); extern void require_char_ws (char expected); -extern void read_name (struct md_name *); +extern void require_word_ws (const char *expected); +extern file_location read_name (struct md_name *); +extern file_location read_name_or_nil (struct md_name *); extern char *read_quoted_string (void); extern char *read_string (int); extern int n_comma_elts (const char *); diff --git a/gcc/read-rtl-function.c b/gcc/read-rtl-function.c new file mode 100644 index 0000000..a108d5e --- /dev/null +++ b/gcc/read-rtl-function.c @@ -0,0 +1,1197 @@ +/* read-rtl-function.c - Reader for RTL function dumps + Copyright (C) 2016 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "target.h" +#include "tree.h" +#include "gimple-expr.h" +#include "diagnostic.h" +#include "opts.h" +#include "fold-const.h" +#include "gimplify.h" +#include "stor-layout.h" +#include "debug.h" +#include "convert.h" +#include "langhooks.h" +#include "langhooks-def.h" +#include "common/common-target.h" +#include "read-md.h" +#include +#include "rtl.h" +#include "cfghooks.h" +#include "stringpool.h" +#include "function.h" +#include "tree-cfg.h" +#include "cfg.h" +#include "basic-block.h" +#include "cfgrtl.h" +#include "emit-rtl.h" +#include "cgraph.h" +#include "tree-pass.h" +#include "context.h" +#include "pass_manager.h" +#include "toplev.h" +#include "bitmap.h" +#include "df.h" +#include "regs.h" +#include "varasm.h" +#include "insn-addr.h" +#include "read-rtl-function.h" +#include "selftest.h" +#include "selftest-rtl.h" + +class function_reader; + +/* Abstract base class for recording post-processing steps that must be + done after reading a .rtl file. */ + +class fixup +{ + public: + fixup (file_location loc, rtx x) + : m_loc (loc), m_rtx (x) + {} + virtual ~fixup () {} + + virtual void apply (function_reader *reader) const = 0; + + protected: + file_location m_loc; + rtx m_rtx; +}; + +/* An abstract subclass of fixup for post-processing steps that + act on a specific operand of a specific instruction. */ + +class operand_fixup : public fixup +{ + public: + operand_fixup (file_location loc, rtx insn, int operand_idx) + : fixup (loc, insn), m_operand_idx (operand_idx) + {} + + protected: + int m_operand_idx; +}; + +/* A concrete subclass of operand_fixup: fixup an rtx_insn * + field (NEXT_INSN/PREV_INSN) based on an integer UID. */ + +class fixup_insn_uid : public operand_fixup +{ + public: + fixup_insn_uid (file_location loc, rtx insn, int operand_idx, int insn_uid) + : operand_fixup (loc, insn, operand_idx), + m_insn_uid (insn_uid) + {} + + void apply (function_reader *reader) const; + + private: + int m_insn_uid; +}; + +/* A concrete subclass of operand_fixup: fix up a basic_block + pointer field based on an integer block ID. */ + +class fixup_bb : public operand_fixup +{ + public: + fixup_bb (file_location loc, rtx insn, int operand_idx, int bb_idx) + : operand_fixup (loc, insn, operand_idx), + m_bb_idx (bb_idx) + {} + + void apply (function_reader *reader) const; + + private: + int m_bb_idx; +}; + +/* A concrete subclass of operand_fixup: fix up a + NOTE_INSN_BASIC_BLOCK based on an integer block ID. */ + +class fixup_note_insn_basic_block : public operand_fixup +{ + public: + fixup_note_insn_basic_block (file_location loc, rtx insn, int operand_idx, + int bb_idx) + : operand_fixup (loc, insn, operand_idx), + m_bb_idx (bb_idx) + {} + + void apply (function_reader *reader) const; + + private: + int m_bb_idx; +}; + +/* A concrete subclass of operand_fixup: fix up the JUMP_LABEL + of an insn based on a label name. */ + +class fixup_jump_label : public operand_fixup +{ + public: + fixup_jump_label (file_location loc, rtx insn, + int operand_idx, + const char *label) + : operand_fixup (loc, insn, operand_idx), + m_label (xstrdup (label)) + {} + + ~fixup_jump_label () { free (m_label); } + + void apply (function_reader *reader) const; + + private: + char *m_label; +}; + +/* A concrete subclass of fixup (not operand_fixup): fix up + the expr of an rtx (REG or MEM) based on a textual dump. */ + +class fixup_expr : public fixup +{ + public: + fixup_expr (file_location loc, rtx x, const char *desc) + : fixup (loc, x), + m_desc (xstrdup (desc)) + {} + + ~fixup_expr () { free (m_desc); } + + void apply (function_reader *reader) const; + + private: + char *m_desc; +}; + +/* Subclass of rtx_reader for reading function dumps. */ + +class function_reader : public rtx_reader +{ + public: + function_reader (rtl_reader_policy *policy); + ~function_reader (); + + void handle_unknown_directive (file_location, const char *); + + void add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx, + int insn_uid); + + void add_fixup_bb (file_location loc, rtx insn, + int operand_idx, int bb_idx); + + void add_fixup_note_insn_basic_block (file_location loc, rtx insn, + int operand_idx, int bb_idx); + + void add_fixup_source_location (file_location loc, rtx insn, + int operand_idx, + const char *filename, int lineno); + + void add_fixup_jump_label (file_location loc, rtx insn, + int operand_idx, + const char *label); + + void add_fixup_expr (file_location loc, rtx x, + const char *desc); + + void create_function (); + void apply_fixups (); + void create_cfg_edges (); + + rtx_insn **get_insn_by_uid (int uid); + + rtx_insn *get_first_insn () const { return m_first_insn; } + + tree parse_mem_expr (const char *desc); + + private: + void create_cfg_and_basic_blocks (); + + private: + struct uid_hash : int_hash {}; + hash_map m_insns_by_uid; + auto_vec m_fixups; + bitmap_head m_bb_indices; + rtx_insn *m_first_insn; + auto_vec m_fake_scope; +}; + +/* Return a textual description of the given operand of the given rtx. */ + +static const char * +get_operand_name (rtx insn, int operand_idx) +{ + gcc_assert (is_a (insn)); + switch (operand_idx) + { + case 0: + return "PREV_INSN"; + case 1: + return "NEXT_INSN"; + default: + return NULL; + } +} + +/* Fixup an rtx_insn * field (NEXT_INSN/PREV_INSN) based on an integer + UID. */ + +void +fixup_insn_uid::apply (function_reader *reader) const +{ + rtx_insn **insn_from_uid = reader->get_insn_by_uid (m_insn_uid); + if (insn_from_uid) + XEXP (m_rtx, m_operand_idx) = *insn_from_uid; + else + { + const char *op_name = get_operand_name (m_rtx, m_operand_idx); + if (op_name) + error_at (m_loc, + "insn with UID %i not found for operand %i (`%s') of insn %i", + m_insn_uid, m_operand_idx, op_name, INSN_UID (m_rtx)); + else + error_at (m_loc, + "insn with UID %i not found for operand %i of insn %i", + m_insn_uid, m_operand_idx, INSN_UID (m_rtx)); + } +} + +/* Fix up a basic_block pointer field based on an integer block ID. + Update BB_HEAD and BB_END of the block as appropriate, as if the insn + was being added to the end of the block. */ + +void +fixup_bb::apply (function_reader */*reader*/) const +{ + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, m_bb_idx); + gcc_assert (bb); + XBBDEF (m_rtx, m_operand_idx) = bb; + + rtx_insn *insn = as_a (m_rtx); + if (!BB_HEAD (bb)) + BB_HEAD (bb) = insn; + BB_END (bb) = insn; +} + +/* Fix up a NOTE_INSN_BASIC_BLOCK based on an integer block ID. */ + +void +fixup_note_insn_basic_block::apply (function_reader */*reader*/) const +{ + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, m_bb_idx); + gcc_assert (bb); + NOTE_BASIC_BLOCK (m_rtx) = bb; +} + +/* Fix up the JUMP_LABEL of an insn based on a label name. */ + +void +fixup_jump_label::apply (function_reader *reader) const +{ + if (0 == strcmp (m_label, "return")) + JUMP_LABEL (m_rtx) = ret_rtx; + else if (0 == strcmp (m_label, "simple_return")) + JUMP_LABEL (m_rtx) = simple_return_rtx; + else + { + int label_uid = atoi (m_label); + rtx_insn **insn_from_uid = reader->get_insn_by_uid (label_uid); + if (insn_from_uid) + JUMP_LABEL (m_rtx) = *insn_from_uid; + else + error_at (m_loc, + "insn with UID %i not found for operand %i (`%s') of insn %i", + label_uid, m_operand_idx, "JUMP_LABEL", INSN_UID (m_rtx)); + } +} + +/* Parse a tree dump for MEM_EXPR and turn it back into a tree. + We handle "", but for anything else we "cheat" by building a + global VAR_DECL of type "int" with that name (returning the same global + for a name if we see the same name more than once). */ + +tree +function_reader::parse_mem_expr (const char *desc) +{ + if (0 == strcmp (desc, "")) + { + tree fndecl = cfun->decl; + return DECL_RESULT (fndecl); + } + + /* FIXME: use a hash rather than linear search. */ + int i; + tree t; + FOR_EACH_VEC_ELT (m_fake_scope, i, t) + if (0 == strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (t)))) + return t; + + /* Not found? Create it. + This allows mimicing of real data but avoids having to specify + e.g. names of locals, params etc. + Though this way we don't know if we have a PARM_DECL vs a VAR_DECL, + and we don't know the types. Fake it by making everything be + a VAR_DECL of "int" type. */ + t = build_decl (UNKNOWN_LOCATION, VAR_DECL, + get_identifier (desc), + integer_type_node); + m_fake_scope.safe_push (t); + return t; +} + +/* Fix up the expr of an rtx (REG or MEM) based on a textual dump. */ + +void +fixup_expr::apply (function_reader *reader) const +{ + tree expr = reader->parse_mem_expr (m_desc); + switch (GET_CODE (m_rtx)) + { + case REG: + set_reg_attrs_for_decl_rtl (expr, m_rtx); + break; + case MEM: + set_mem_expr (m_rtx, expr); + break; + default: + gcc_unreachable (); + } +} + +/* class function_reader : public rtx_reader */ + +/* function_reader's constructor. */ + +function_reader::function_reader (rtl_reader_policy *policy) +: rtx_reader (policy), + m_first_insn (NULL) +{ + bitmap_initialize (&m_bb_indices, NULL); + bitmap_set_bit (&m_bb_indices, ENTRY_BLOCK); + bitmap_set_bit (&m_bb_indices, EXIT_BLOCK); +} + +/* function_reader's destructor. */ + +function_reader::~function_reader () +{ + int i; + fixup *f; + FOR_EACH_VEC_ELT (m_fixups, i, f) + delete f; +} + +/* Implementation of rtx_reader::handle_unknown_directive. + Parse rtx instructions by calling read_rtx_code, calling + set_first_insn and set_last_insn as appropriate. */ + +void +function_reader::handle_unknown_directive (file_location /*start_loc*/, + const char *name) +{ + rtx x = read_rtx_code (name); + if (!x) + return; + rtx_insn *insn = as_a (x); + set_last_insn (insn); + if (!m_first_insn) + { + m_first_insn = insn; + set_first_insn (insn); + } + m_insns_by_uid.put (INSN_UID (insn), insn); +} + +/* Implementation of rtx_reader::add_fixup_insn_uid. + Record the information for later post-processing. */ + +void +function_reader::add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx, + int insn_uid) +{ + m_fixups.safe_push (new fixup_insn_uid (loc, insn, operand_idx, insn_uid)); +} + +/* Implementation of rtx_reader::add_fixup_bb. + Record the information for later post-processing. */ + +void +function_reader::add_fixup_bb (file_location loc, rtx insn, int operand_idx, + int bb_idx) +{ + bitmap_set_bit (&m_bb_indices, bb_idx); + m_fixups.safe_push (new fixup_bb (loc, insn, operand_idx, bb_idx)); +} + +/* Implementation of rtx_reader::add_fixup_note_insn_basic_block. + Record the information for later post-processing. */ + +void +function_reader::add_fixup_note_insn_basic_block (file_location loc, rtx insn, + int operand_idx, int bb_idx) +{ + m_fixups.safe_push (new fixup_note_insn_basic_block (loc, insn, operand_idx, + bb_idx)); +} + +/* Implementation of rtx_reader::add_fixup_source_location. + Record the information for later post-processing. */ +void +function_reader::add_fixup_source_location (file_location, rtx, + int, const char *, int) +{ + /* Empty for now. */ +} + +/* Implementation of rtx_reader::add_fixup_jump_label. + Record the information for later post-processing. */ + +void +function_reader::add_fixup_jump_label (file_location loc, rtx insn, + int operand_idx, + const char *label) +{ + m_fixups.safe_push (new fixup_jump_label (loc, insn, operand_idx, label)); +} + +/* Implementation of rtx_reader::add_fixup_expr. + Record the information for later post-processing. */ + +void +function_reader::add_fixup_expr (file_location loc, rtx insn, + const char *desc) +{ + gcc_assert (desc); + /* Fail early if the RTL reader erroneously hands us an int. */ + gcc_assert (!ISDIGIT (desc[0])); + + m_fixups.safe_push (new fixup_expr (loc, insn, desc)); +} + +/* Set up state for the function *before* fixups are applied. + + Create "cfun" and a decl for the function. + For the moment, every function decl is hardcoded as + int test_1 (int i, int j, int k); + Set up various other state: + - state set up by expand_function_start (e.g. crtl->return_rtx). + - the cfg and basic blocks (edges are created later, *after* fixups + are applied). + - add the function to the callgraph. */ + +void +function_reader::create_function () +{ + /* Currently we assume cfgrtl mode, rather than cfglayout mode. */ + if (0) + cfg_layout_rtl_register_cfg_hooks (); + else + rtl_register_cfg_hooks (); + + /* Create cfun. */ + tree fn_name = get_identifier ("test_1"); + tree int_type = integer_type_node; + tree return_type = int_type; + tree arg_types[3] = {int_type, int_type, int_type}; + tree fn_type = build_function_type_array (return_type, 3, arg_types); + tree fndecl = build_decl_stat (UNKNOWN_LOCATION, FUNCTION_DECL, fn_name, + fn_type); + tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, + return_type); + DECL_ARTIFICIAL (resdecl) = 1; + DECL_IGNORED_P (resdecl) = 1; + DECL_RESULT (fndecl) = resdecl; + allocate_struct_function (fndecl, false); + /* This sets cfun. */ + + /* Normally, various state gets set up by expand_function_start, e.g. + crtl->return_rtx, based on DECL_RESULT (fndecl). + We call it here to ensure the state is set up, with emit_insns as + false, so no new instructions are emitted. */ + current_function_decl = fndecl; + expand_function_start (fndecl, false); + + create_cfg_and_basic_blocks (); + cfun->curr_properties = (PROP_cfg | PROP_rtl); + + /* Do we need this to force cgraphunit.c to output the function? */ + DECL_EXTERNAL (fndecl) = 0; + DECL_PRESERVE_P (fndecl) = 1; + + /* Add to cgraph. */ + { + /* cgraph_node::add_new_function does additional processing + based on symtab->state. We need to avoid it attempting to gimplify + things. Temporarily putting it in the PARSING state appears to + achieve this. */ + enum symtab_state old_state = symtab->state; + symtab->state = PARSING; + cgraph_node::add_new_function (fndecl, true /*lowered*/); + /* Reset the state. */ + symtab->state = old_state; + } +} + +/* Apply all of the recorded fixups. */ + +void +function_reader::apply_fixups () +{ + /* line_table should now be populated; every deferred_location should + now have an m_srcloc. */ + + int i; + fixup *f; + FOR_EACH_VEC_ELT (m_fixups, i, f) + f->apply (this); +} + +/* Given a UID value, try to locate a pointer to the corresponding + rtx_insn *, or NULL if if can't be found. */ + +rtx_insn ** +function_reader::get_insn_by_uid (int uid) +{ + return m_insns_by_uid.get (uid); +} + +/* Create cfun's CFG and populate with blocks, a helper + function for function_reader::create_function (). + + The edges are created later on, after fixups are applied. + + We can't call create_basic_block and use the regular RTL block-creation + hooks, since this creates NOTE_INSN_BASIC_BLOCK instances. We don't + want to do that; we want to use the notes we were provided with. + + The term "index" has two meanings for basic blocks in a CFG: + (a) the "index" field within struct basic_block_def. + (b) the index of a basic_block within the cfg's x_basic_block_info + vector, as accessed via BASIC_BLOCK_FOR_FN. + + These can get out-of-sync when basic blocks are optimized away. + They get back in sync by "compact_blocks". + We make the assumption that the CFG has been compacted, and + so we reconstruct cfun->cfg->x_basic_block_info->m_vecdata with NULL + values in it for any missing basic blocks, so that (a) == (b) for + all of the blocks we create. The doubly-linked list of basic + blocks (next_bb/prev_bb) skips over these "holes". */ + +void +function_reader::create_cfg_and_basic_blocks () +{ + /* Create bare-bones cfg. This creates the entry and exit blocks. */ + init_empty_tree_cfg_for_function (cfun); + ENTRY_BLOCK_PTR_FOR_FN (cfun)->flags |= BB_RTL; + EXIT_BLOCK_PTR_FOR_FN (cfun)->flags |= BB_RTL; + + /* Ensure that the vector of basic_block pointers is big enough. */ + unsigned highest_bb_idx = bitmap_last_set_bit (&m_bb_indices); + size_t new_size = highest_bb_idx + 1; + if (basic_block_info_for_fn (cfun)->length () < new_size) + vec_safe_grow_cleared (basic_block_info_for_fn (cfun), new_size); + + /* Populate the vector. */ + unsigned bb_idx; + bitmap_iterator bi; + basic_block after = ENTRY_BLOCK_PTR_FOR_FN (cfun); + gcc_assert (after); + EXECUTE_IF_SET_IN_BITMAP (&m_bb_indices, 0, bb_idx, bi) + { + /* The entry and exit blocks were already created above. */ + if (bb_idx == ENTRY_BLOCK || bb_idx == EXIT_BLOCK) + { + basic_block bb = BASIC_BLOCK_FOR_FN (cfun, bb_idx); + init_rtl_bb_info (bb); + continue; + } + basic_block bb = alloc_block (); + init_rtl_bb_info (bb); + bb->index = bb_idx; + bb->flags = BB_NEW | BB_RTL; + link_block (bb, after); + + n_basic_blocks_for_fn (cfun)++; + SET_BASIC_BLOCK_FOR_FN (cfun, bb_idx, bb); + BB_SET_PARTITION (bb, BB_UNPARTITIONED); + + /* Tag the block so that we know it has been used when considering + other basic block notes. */ + bb->aux = bb; + + after = bb; + } + + last_basic_block_for_fn (cfun) = highest_bb_idx + 1; +} + +/* Visit every LABEL_REF within JUMP_INSN, calling CB on it, providing it + with USER_DATA. + FIXME: is there a preexisting way to do this? */ + +static void +for_each_label_ref (rtx_jump_insn *jump_insn, + void (*cb) (rtx_jump_insn *jump_insn, + rtx label_ref, + void *user_data), + void *user_data) +{ + if (any_condjump_p (jump_insn)) + { + rtx label_ref = condjump_label (jump_insn); + cb (jump_insn, label_ref, user_data); + return; + } + + if (simplejump_p (jump_insn)) + { + rtx label_ref = SET_SRC (PATTERN (jump_insn)); + cb (jump_insn, label_ref, user_data); + return; + } + + rtx_jump_table_data *tablep; + if (tablejump_p (jump_insn, NULL, &tablep)) + { + gcc_assert (tablep); + rtvec labels = tablep->get_labels (); + int i, veclen = GET_NUM_ELEM (labels); + for (i = 0; i < veclen; ++i) + { + rtx label_ref = RTVEC_ELT (labels, i); + cb (jump_insn, label_ref, user_data); + } + return; + } + + if (ANY_RETURN_P (PATTERN (jump_insn))) + return; + + /* TODO: something else? */ + gcc_unreachable (); +} + +/* Create an edge from SRC to DST, with the given flags. */ + +static void +add_edge (basic_block src, basic_block dst, int flags) +{ + if (0) + fprintf (stderr, "making edge %i->%i\n", + src->index, dst->index); + unchecked_make_edge (src, dst, flags); +} + +/* Edge-creation callback for function_reader::create_cfg_edges. */ + +static void +add_edge_cb (rtx_jump_insn *jump_insn, rtx label_ref, void */*user_data*/) +{ + gcc_assert (jump_insn); + gcc_assert (label_ref); + rtx_insn *label_insn = as_a (LABEL_REF_LABEL (label_ref)); + add_edge (BLOCK_FOR_INSN (jump_insn), BLOCK_FOR_INSN (label_insn), 0); +} + +/* Create edges within cfun's CFG, by examining instructions in the + basic blocks and reconstructing the edges accordingly. + This is done after fixups are applied, since the latter is responsible + for setting up BB_HEAD and BB_END within each basic block. + + This assumes cfgrtl mode, in which the edges are implicit from + the jump instructions. It won't work for cfglayout mode, which + represents unconditional jumps purely as edges within the CFG, + without instructions, and this information isn't (currently) + written out to dumps. */ + +void +function_reader::create_cfg_edges () +{ + /* Create edge from ENTRY_BLOCK to block of first insn. */ + basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun); + add_edge (entry, entry->next_bb, EDGE_FALLTHRU); + + /* Create other edges. + + The edge from the block of last insn to EXIT_BLOCK is created + below, by fall-through from the end of its block. */ + basic_block bb; + FOR_ALL_BB_FN (bb, cfun) + { + if (!BB_HEAD (bb)) + continue; + rtx_insn *end_insn = BB_END (bb); + if (rtx_jump_insn *jump_insn = dyn_cast (end_insn)) + { + if (0) + fprintf (stderr, "bb %i ends in jump\n", bb->index); + if (!any_uncondjump_p (end_insn)) + { + /* Add fallthrough edge first. */ + gcc_assert (bb->next_bb); + add_edge (bb, bb->next_bb, EDGE_FALLTHRU); + } + for_each_label_ref (jump_insn, add_edge_cb, NULL); + } + else + { + if (0) + fprintf (stderr, "bb %i ends in non-jump\n", bb->index); + if (bb->next_bb != NULL) + { + /* Add fallthrough edge. */ + gcc_assert (bb->next_bb); + add_edge (bb, bb->next_bb, EDGE_FALLTHRU); + } + } + } +} + +static void +postprocess (function_reader *reader) +{ + reader->create_function (); + reader->apply_fixups (); + reader->create_cfg_edges (); + + /* Ensure x_cur_insn_uid is 1 more than the biggest insn UID seen. + This is normally updated by the various make_*insn_raw functions. */ + { + rtx_insn *insn; + int max_uid = 0; + for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) + max_uid = MAX (max_uid, INSN_UID (insn)); + crtl->emit.x_cur_insn_uid = max_uid + 1; + } + + crtl->init_stack_alignment (); +} + +bool +read_rtl_function_body (int argc, const char **argv, + bool (*parse_opt) (const char *), + rtl_reader_policy *policy) +{ + initialize_rtl (); + init_emit (); + init_varasm_status (); + + function_reader reader (policy); + if (!reader.read_md_files (argc, argv, parse_opt)) + return false; + + postprocess (&reader); + + return true; +} + +#if CHECKING_P + +namespace selftest { + +/* Verify that we can load RTL dumps. */ + +static void +test_loading_dump_fragment_1 () +{ + /* Taken from + gcc/testsuite/gcc.dg/asr_div1.c -O2 -fdump-rtl-all -mtune=cortex-a53 + for aarch64, hand editing the prev/next insns to 0 as needed, and + editing whitespace to avoid over-long lines. */ + // TODO: filter on target? + const char *input_dump + = ("(insn 8 0 9 2 (set (reg:DI 78)\n" + " (lshiftrt:DI (reg:DI 76)\n" + " (const_int 32 [0x20])))\n" + " ../../src/gcc/testsuite/gcc.dg/asr_div1.c:14\n" + " 641 {*aarch64_lshr_sisd_or_int_di3}\n" + " (expr_list:REG_DEAD (reg:DI 76)\n" + " (nil)))\n" + "(insn 9 8 0 2 (set (reg:SI 79)\n" + " (ashiftrt:SI (subreg:SI (reg:DI 78) 0)\n" + " (const_int 3 [0x3])))\n" + " ../../src/gcc/testsuite/gcc.dg/asr_div1.c:14\n" + " 642 {*aarch64_ashr_sisd_or_int_si3}\n" + " (expr_list:REG_DEAD (reg:DI 78)\n" + " (nil)))\n"); + const int dumped_first_pseudo_regno = 76; + rtl_dump_test t (input_dump, dumped_first_pseudo_regno); + + /* Verify that the insns were loaded correctly. */ + rtx_insn *insn_8 = get_insns (); + ASSERT_TRUE (insn_8); + ASSERT_EQ (8, INSN_UID (insn_8)); + ASSERT_EQ (INSN, GET_CODE (insn_8)); + ASSERT_EQ (SET, GET_CODE (PATTERN (insn_8))); + ASSERT_EQ (NULL, PREV_INSN (insn_8)); + + rtx_insn *insn_9 = NEXT_INSN (insn_8); + ASSERT_TRUE (insn_9); + ASSERT_EQ (9, INSN_UID (insn_9)); + ASSERT_EQ (INSN, GET_CODE (insn_9)); + ASSERT_EQ (insn_8, PREV_INSN (insn_9)); + ASSERT_EQ (NULL, NEXT_INSN (insn_9)); + + /* Verify that registers were remapped. */ + rtx insn_8_dest = SET_DEST (PATTERN (insn_8)); + ASSERT_EQ (REG, GET_CODE (insn_8_dest)); + ASSERT_EQ (t.effective_regno (78), REGNO (insn_8_dest)); + rtx insn_8_src = SET_SRC (PATTERN (insn_8)); + ASSERT_EQ (LSHIFTRT, GET_CODE (insn_8_src)); + rtx reg = XEXP (insn_8_src, 0); + ASSERT_EQ (REG, GET_CODE (reg)); + ASSERT_EQ (t.effective_regno (76), REGNO (reg)); + + /* Verify that get_insn_by_uid works. */ + ASSERT_EQ (insn_8, get_insn_by_uid (8)); + ASSERT_EQ (insn_9, get_insn_by_uid (9)); + + /* Verify that basic blocks were created. */ + ASSERT_EQ (2, BLOCK_FOR_INSN (insn_8)->index); + ASSERT_EQ (2, BLOCK_FOR_INSN (insn_9)->index); + + /* Verify that a sane CFG was created. */ + ASSERT_TRUE (cfun); + verify_three_block_rtl_cfg (cfun); + basic_block bb2 = BASIC_BLOCK_FOR_FN (cfun, 2); + ASSERT_TRUE (bb2 != NULL); + ASSERT_EQ (BB_RTL, bb2->flags & BB_RTL); + ASSERT_EQ (2, bb2->index); + ASSERT_EQ (insn_8, BB_HEAD (bb2)); + ASSERT_EQ (insn_9, BB_END (bb2)); +} + +/* Verify loading another RTL dump; this time a dump of copying + a param on x86_64 from a hard reg into the frame. */ + +static void +test_loading_dump_fragment_2 () +{ + const char *input_dump + = ("(insn 2 0 0 2\n" + " (set (mem/c:SI\n" + " (plus:DI\n" + " (reg/f:DI 20 frame)\n" + " (const_int -4 [0xfffffffffffffffc]))\n" + " [1 i+0 S4 A32])\n" + " (reg:SI 5 di [ i ])) test.c:2 -1\n" + " (nil))\n"); + rtl_dump_test t (input_dump); + + rtx_insn *insn = get_insn_by_uid (2); + + /* The block structure and indentation here is purely for + readability; it mirrors the structure of the rtx. */ + tree mem_expr; + { + rtx pat = PATTERN (insn); + ASSERT_EQ (SET, GET_CODE (pat)); + { + rtx dest = SET_DEST (pat); + ASSERT_EQ (MEM, GET_CODE (dest)); + /* Verify the "/c" was parsed. */ + ASSERT_TRUE (RTX_FLAG (dest, call)); + ASSERT_EQ (SImode, GET_MODE (dest)); + { + rtx addr = XEXP (dest, 0); + ASSERT_EQ (PLUS, GET_CODE (addr)); + ASSERT_EQ (DImode, GET_MODE (addr)); + { + rtx lhs = XEXP (addr, 0); + ASSERT_EQ (REG, GET_CODE (lhs)); + /* Verify the "/f" was parsed. */ + ASSERT_TRUE (RTX_FLAG (lhs, frame_related)); + ASSERT_EQ (DImode, GET_MODE (lhs)); + } + { + rtx rhs = XEXP (addr, 1); + ASSERT_EQ (CONST_INT, GET_CODE (rhs)); + ASSERT_EQ (-4, INTVAL (rhs)); + } + } + /* Verify the "[1 i+0 S4 A32]" was parsed. */ + ASSERT_EQ (1, MEM_ALIAS_SET (dest)); + /* "i" should have been handled by synthesizing a global int + variable named "i". */ + mem_expr = MEM_EXPR (dest); + ASSERT_NE (mem_expr, NULL); + ASSERT_EQ (VAR_DECL, TREE_CODE (mem_expr)); + ASSERT_EQ (integer_type_node, TREE_TYPE (mem_expr)); + ASSERT_EQ (IDENTIFIER_NODE, TREE_CODE (DECL_NAME (mem_expr))); + ASSERT_STREQ ("i", IDENTIFIER_POINTER (DECL_NAME (mem_expr))); + /* "+0". */ + ASSERT_TRUE (MEM_OFFSET_KNOWN_P (dest)); + ASSERT_EQ (0, MEM_OFFSET (dest)); + /* "S4". */ + ASSERT_EQ (4, MEM_SIZE (dest)); + /* "A32. */ + ASSERT_EQ (32, MEM_ALIGN (dest)); + } + { + rtx src = SET_SRC (pat); + ASSERT_EQ (REG, GET_CODE (src)); + ASSERT_EQ (SImode, GET_MODE (src)); + ASSERT_EQ (5, REGNO (src)); + tree reg_expr = REG_EXPR (src); + /* "i" here should point to the same var as for the MEM_EXPR. */ + ASSERT_EQ (reg_expr, mem_expr); + } + } +} + +/* Verify that CODE_LABEL insns are loaded correctly. */ + +static void +test_loading_labels () +{ + const char *input_dump + = ("(code_label 1 0 2 6 3 (nil) [0 uses])\n" + "(code_label 2 1 0 6 3 (\"some_label_name\") [57 uses])\n"); + rtl_dump_test t (input_dump); + + rtx_insn *insn_1 = get_insn_by_uid (1); + ASSERT_EQ (CODE_LABEL, GET_CODE (insn_1)); + ASSERT_EQ (NULL, LABEL_NAME (insn_1)); + ASSERT_EQ (0, LABEL_NUSES (insn_1)); + + rtx_insn *insn_2 = get_insn_by_uid (2); + ASSERT_EQ (CODE_LABEL, GET_CODE (insn_2)); + ASSERT_STREQ ("some_label_name", LABEL_NAME (insn_2)); + ASSERT_EQ (57, LABEL_NUSES (insn_2)); + + /* Ensure that label names read from a dump are GC-managed + and are found through the insn. */ + forcibly_ggc_collect (); + ASSERT_TRUE (ggc_marked_p (insn_2)); + ASSERT_TRUE (ggc_marked_p (LABEL_NAME (insn_2))); +} + +/* Verify that the loader copes with an insn with a mode. */ + +static void +test_loading_insn_with_mode () +{ + const char *input_dump + = "(insn:TI 31 0 0 2 (set (reg:SI 100) (reg:SI 101)) -1 (nil))"; + const int dumped_first_pseudo_regno = 100; + rtl_dump_test t (input_dump, dumped_first_pseudo_regno); + rtx_insn *insn = get_insn_by_uid (31); + ASSERT_EQ (INSN, GET_CODE (insn)); + + /* Verify that the "TI" mode was set from "insn:TI". */ + ASSERT_EQ (TImode, GET_MODE (insn)); +} + +/* Verify that the loader copes with a jump_insn to a label_ref. */ + +static void +test_loading_jump_to_label_ref () +{ + const char *input_dump + = ("(jump_insn 1 0 2 4 (set (pc)\n" + " (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1\n" + " (nil)\n" + " -> 3)\n" + "(barrier 2 1 3)\n" + "(code_label 3 2 0 5 2 (nil) [1 uses])\n"); + rtl_dump_test t (input_dump); + + rtx_insn *jump_insn = get_insn_by_uid (1); + ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn)); + + rtx_insn *barrier = get_insn_by_uid (2); + ASSERT_EQ (BARRIER, GET_CODE (barrier)); + + rtx_insn *code_label = get_insn_by_uid (3); + ASSERT_EQ (CODE_LABEL, GET_CODE (code_label)); + + /* Verify the jump_insn. */ + ASSERT_EQ (4, BLOCK_FOR_INSN (jump_insn)->index); + ASSERT_EQ (SET, GET_CODE (PATTERN (jump_insn))); + /* Ensure that the "(pc)" is using the global singleton. */ + ASSERT_EQ (pc_rtx, SET_DEST (PATTERN (jump_insn))); + // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^ + rtx label_ref = SET_SRC (PATTERN (jump_insn)); + ASSERT_EQ (LABEL_REF, GET_CODE (label_ref)); + ASSERT_EQ (code_label, LABEL_REF_LABEL (label_ref)); + ASSERT_EQ (code_label, JUMP_LABEL (jump_insn)); + + /* Verify the code_label. */ + ASSERT_EQ (5, BLOCK_FOR_INSN (code_label)->index); + ASSERT_EQ (NULL, LABEL_NAME (code_label)); + ASSERT_EQ (1, LABEL_NUSES (code_label)); + + // TODO: verify the generated CFG +} + +/* Verify that the loader copes with a jump_insn to a label_ref + marked "return". */ + +static void +test_loading_jump_to_return () +{ + const char *input_dump + = ("(jump_insn 1 0 2 4 (set (pc)\n" + " (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1\n" + " (nil)\n" + " -> return)\n" + "(barrier 2 1 3)\n" + "(code_label 3 2 0 5 2 (nil) [1 uses])\n"); + rtl_dump_test t (input_dump); + + rtx_insn *jump_insn = get_insn_by_uid (1); + ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn)); + ASSERT_EQ (ret_rtx, JUMP_LABEL (jump_insn)); + // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^ +} + +/* Verify that the loader copes with a jump_insn to a label_ref + marked "simple_return". */ + +static void +test_loading_jump_to_simple_return () +{ + const char *input_dump + = ("(jump_insn 1 0 2 4 (set (pc)\n" + " (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1\n" + " (nil)\n" + " -> simple_return)\n" + "(barrier 2 1 3)\n" + "(code_label 3 2 0 5 2 (nil) [1 uses])\n"); + rtl_dump_test t (input_dump); + + rtx_insn *jump_insn = get_insn_by_uid (1); + ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn)); + ASSERT_EQ (simple_return_rtx, JUMP_LABEL (jump_insn)); + // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^ +} + +/* Verify that the loader copes with a NOTE_INSN_BASIC_BLOCK. */ + +static void +test_loading_note_insn_basic_block () +{ + const char *input_dump + = "(note 1 0 0 2 [bb 2] NOTE_INSN_BASIC_BLOCK)\n"; + rtl_dump_test t (input_dump); + + rtx_insn *note = get_insn_by_uid (1); + ASSERT_EQ (NOTE, GET_CODE (note)); + ASSERT_EQ (2, BLOCK_FOR_INSN (note)->index); + + ASSERT_EQ (NOTE_INSN_BASIC_BLOCK, NOTE_KIND (note)); + ASSERT_EQ (2, NOTE_BASIC_BLOCK (note)->index); + ASSERT_EQ (BASIC_BLOCK_FOR_FN (cfun, 2), NOTE_BASIC_BLOCK (note)); +} + +/* Verify that the loader copes with a NOTE_INSN_DELETED. */ + +static void +test_loading_note_insn_deleted () +{ + const char *input_dump + = "(note 1 0 0 (nil) NOTE_INSN_DELETED)\n"; + rtl_dump_test t (input_dump); + + rtx_insn *note = get_insn_by_uid (1); + ASSERT_EQ (NOTE, GET_CODE (note)); + ASSERT_EQ (NOTE_INSN_DELETED, NOTE_KIND (note)); +} + +/* Verify that the const_int values are consolidated, since + pointer equality corresponds to value equality. + TODO: do this for all in CASE_CONST_UNIQUE. */ + +static void +test_loading_const_int () +{ + const char *input_dump + = ("(insn 100 0 101 2\n" + " (set (reg:SI 100) (const_int 0 [0x0]))\n" + " test.c:2 -1 (nil))\n" + "(insn 101 100 102 2\n" + " (set (reg:SI 101) (const_int 1 [0x1]))\n" + " test.c:2 -1 (nil))\n" + "(insn 102 101 103 2\n" + " (set (reg:SI 102) (const_int -1 [0xffffffff]))\n" + " test.c:2 -1 (nil))\n" + "(insn 103 102 0 2\n" + " (set (reg:SI 103) (const_int 256 [0x100]))\n" + " test.c:2 -1 (nil))\n"); + rtl_dump_test t (input_dump); + + /* Verify that const_int values below MAX_SAVED_CONST_INT use + the global values. */ + ASSERT_EQ (const0_rtx, SET_SRC (PATTERN (get_insn_by_uid (100)))); + ASSERT_EQ (const1_rtx, SET_SRC (PATTERN (get_insn_by_uid (101)))); + ASSERT_EQ (constm1_rtx, SET_SRC (PATTERN (get_insn_by_uid (102)))); + + /* Verify that other const_int values are consolidated. */ + rtx int256 = gen_rtx_CONST_INT (SImode, 256); + ASSERT_EQ (int256, SET_SRC (PATTERN (get_insn_by_uid (103)))); +} + +/* Verify that the loader copes with a SYMBOL_REF. */ + +static void +test_loading_symbol_ref () +{ + const char *input_dump + = ("(insn 1045 0 0 2 (set (reg:SI 480)\n" + " (high:SI (symbol_ref:SI (\"isl_obj_map_vtable\") [flags 0xc0] ))) y.c:12702 -1\n" + " (nil))\n"); + rtl_dump_test t (input_dump); + rtx_insn *insn = get_insn_by_uid (1045); + + rtx high = SET_SRC (PATTERN (insn)); + ASSERT_EQ (HIGH, GET_CODE (high)); + + rtx symbol_ref = XEXP (high, 0); + ASSERT_EQ (SYMBOL_REF, GET_CODE (symbol_ref)); + + /* Verify that "[flags 0xc0]" was parsed. */ + ASSERT_EQ (0xc0, SYMBOL_REF_FLAGS (symbol_ref)); + /* TODO: we don't yet load SYMBOL_REF_DECL. */ +} + +/* Run all of the selftests within this file. */ + +void +read_rtl_function_c_tests () +{ + test_loading_dump_fragment_1 (); + test_loading_dump_fragment_2 (); + test_loading_labels (); + test_loading_insn_with_mode (); + test_loading_jump_to_label_ref (); + test_loading_jump_to_return (); + test_loading_jump_to_simple_return (); + test_loading_note_insn_basic_block (); + test_loading_note_insn_deleted (); + test_loading_const_int (); + test_loading_symbol_ref (); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ diff --git a/gcc/read-rtl-function.h b/gcc/read-rtl-function.h new file mode 100644 index 0000000..5cc0233 --- /dev/null +++ b/gcc/read-rtl-function.h @@ -0,0 +1,29 @@ +/* read-rtl-function.h - Reader for RTL function dumps + Copyright (C) 2016 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef GCC_READ_RTL_FUNCTION_H +#define GCC_READ_RTL_FUNCTION_H + +struct rtl_reader_policy; + +extern bool read_rtl_function_body (int argc, const char **argv, + bool (*parse_opt) (const char *), + rtl_reader_policy *policy); + +#endif /* GCC_READ_RTL_FUNCTION_H */ diff --git a/gcc/read-rtl.c b/gcc/read-rtl.c index eda9382..ce5dbdb 100644 --- a/gcc/read-rtl.c +++ b/gcc/read-rtl.c @@ -17,7 +17,13 @@ You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ +/* This file is compiled twice: once for the generator programs + once for the compiler. */ +#ifdef GENERATOR_FILE #include "bconfig.h" +#else +#include "config.h" +#endif /* Disable rtl checking; it conflicts with the iterator handling. */ #undef ENABLE_RTL_CHECKING @@ -30,6 +36,11 @@ along with GCC; see the file COPYING3. If not see #include "read-md.h" #include "gensupport.h" +#ifndef GENERATOR_FILE +#include "function.h" +#include "emit-rtl.h" +#endif + /* One element in a singly-linked list of (integer, string) pairs. */ struct map_value { struct map_value *next; @@ -106,10 +117,14 @@ htab_t subst_attr_to_iter_map = NULL; const char *current_iterator_name; static void validate_const_int (const char *); -static rtx read_rtx_code (const char *); +static void one_time_initialization (void); static rtx read_nested_rtx (void); static rtx read_rtx_variadic (rtx); +#ifndef GENERATOR_FILE +static rtx consolidate_singletons (rtx); +#endif + /* The mode and code iterator structures. */ static struct iterator_group modes, codes, ints, substs; @@ -181,6 +196,8 @@ apply_int_iterator (void *loc, int value) *(int *)loc = value; } +#ifdef GENERATOR_FILE + /* This routine adds attribute or does nothing depending on VALUE. When VALUE is 1, it does nothing - the first duplicate of original template is kept untouched when it's subjected to a define_subst. @@ -252,6 +269,8 @@ bind_subst_iter_and_attr (const char *iter, const char *attr) *slot = value; } +#endif /* #ifdef GENERATOR_FILE */ + /* Return name of a subst-iterator, corresponding to subst-attribute ATTR. */ static char* @@ -418,6 +437,8 @@ copy_rtx_for_iterators (rtx original) return x; } +#ifdef GENERATOR_FILE + /* Return a condition that must satisfy both ORIGINAL and EXTRA. If ORIGINAL has the form "&& ..." (as used in define_insn_and_splits), assume that EXTRA is already satisfied. Empty strings are treated like "true". */ @@ -581,6 +602,7 @@ apply_iterators (rtx original, vec *queue) } } } +#endif /* #ifdef GENERATOR_FILE */ /* Add a new "mapping" structure to hashtable TABLE. NAME is the name of the mapping and GROUP is the group to which it belongs. */ @@ -655,7 +677,9 @@ initialize_iterators (void) substs.iterators = htab_create (13, leading_string_hash, leading_string_eq_p, 0); substs.find_builtin = find_int; /* We don't use it, anyway. */ +#ifdef GENERATOR_FILE substs.apply_iterator = apply_subst_iterator; +#endif lower = add_mapping (&modes, modes.attrs, "mode"); upper = add_mapping (&modes, modes.attrs, "MODE"); @@ -724,6 +748,8 @@ atoll (const char *p) } #endif + +#ifdef GENERATOR_FILE /* Process a define_conditions directive, starting with the optional space after the "define_conditions". The directive looks like this: @@ -765,6 +791,7 @@ read_conditions (void) add_c_test (expr, value); } } +#endif /* #ifdef GENERATOR_FILE */ static void validate_const_int (const char *string) @@ -861,6 +888,8 @@ record_potential_iterator_use (struct iterator_group *group, void *ptr, } } +#ifdef GENERATOR_FILE + /* Finish reading a declaration of the form: (define... [ ... ]) @@ -1020,14 +1049,7 @@ check_code_iterator (struct mapping *iterator) bool read_rtx (const char *rtx_name, vec *rtxen) { - static bool initialized = false; - - /* Do one-time initialization. */ - if (!initialized) - { - initialize_iterators (); - initialized = true; - } + one_time_initialization (); /* Handle various rtx-related declarations that aren't themselves encoded as rtxes. */ @@ -1082,16 +1104,220 @@ read_rtx (const char *rtx_name, vec *rtxen) return true; } +#endif /* #ifdef GENERATOR_FILE */ + +/* Do one-time initialization. */ + +static void +one_time_initialization (void) +{ + static bool initialized = false; + + if (!initialized) + { + initialize_iterators (); + initialized = true; + } +} + +#ifndef GENERATOR_FILE +static int +parse_reg_note_name (const char *string) +{ + for (int i = 0; i < REG_NOTE_MAX; i++) + if (0 == strcmp (string, GET_REG_NOTE_NAME (i))) + return i; + fatal_with_file_and_line ("unrecognized REG_NOTE name: `%s'", string); +} + +static int +parse_note_insn_name (const char *string) +{ + for (int i = 0; i < NOTE_INSN_MAX; i++) + if (0 == strcmp (string, GET_NOTE_INSN_NAME (i))) + return i; + fatal_with_file_and_line ("unrecognized NOTE_INSN name: `%s'", string); +} + +/* Handle the optional location information written by print_rtx for + instructions. Specifically, operand 4 of instructions (of type "i') + is printed thus: + + if (INSN_HAS_LOCATION (in_insn)) + { + expanded_location xloc = insn_location (in_insn); + fprintf (outfile, " %s:%i", xloc.file, xloc.line); + } + + Hence we need to speculatively read a location of the form + " %s:%i", and unread the content if there wasn't one. + + Assume that filenames can't contain whitespace, and can't + contain ':'. */ + +void maybe_read_location (int operand_idx, rtx insn) +{ + file_location loc = rtx_reader_ptr->get_current_location (); + + /* Skip to first non-whitespace. */ + int ch = read_skip_spaces (); + auto_vec buf; + buf.safe_push (ch); + while (1) + { + int ch = read_char (); + /* If we see a ':', assume we have a filename. */ + if (ch == ':') + { + buf.safe_push ('\0'); + break; + } + buf.safe_push (ch); + + /* If we see a space before ':', assume we don't have a + filename. */ + if (ISSPACE (ch)) + { + while (!buf.is_empty ()) + unread_char (buf.pop ()); + return; + } + } + char *filename = buf.address (); + struct md_name name; + read_name (&name); + + rtx_reader_ptr->add_fixup_source_location (loc, insn, operand_idx, + filename, atoi(name.string)); +} + +/* Consume characters until encountering a character in TERMINATOR_CHARS, + consuming the terminator character if CONSUME_TERMINATOR is true. + Return all characters before the terminator as an allocated buffer. */ + +static char * +read_until (const char *terminator_chars, bool consume_terminator) +{ + int ch = read_skip_spaces (); + unread_char (ch); + auto_vec buf; + while (1) + { + ch = read_char (); + if (strchr (terminator_chars, ch)) + { + if (!consume_terminator) + unread_char (ch); + break; + } + buf.safe_push (ch); + } + buf.safe_push ('\0'); + return xstrdup (buf.address ()); +} + +static void strip_trailing_whitespace (char *desc) +{ + char *terminator = desc + strlen (desc); + while (desc < terminator) + { + terminator--; + if (ISSPACE (*terminator)) + *terminator = '\0'; + else + break; + } +} + +#endif /* #ifndef GENERATOR_FILE */ + +/* Subroutine of read_rtx_code, for parsing zero or more flags. */ + +static void +read_flags (rtx return_rtx) +{ + while (1) + { + int ch = read_char (); + if (ch != '/') + { + unread_char (ch); + break; + } + + int flag_char = read_char (); + switch (flag_char) + { + case 's': + RTX_FLAG (return_rtx, in_struct) = 1; + break; + case 'v': + RTX_FLAG (return_rtx, volatil) = 1; + break; + case 'u': + RTX_FLAG (return_rtx, unchanging) = 1; + break; + case 'f': + RTX_FLAG (return_rtx, frame_related) = 1; + break; + case 'j': + RTX_FLAG (return_rtx, jump) = 1; + break; + case 'c': + RTX_FLAG (return_rtx, call) = 1; + break; + case 'i': + RTX_FLAG (return_rtx, return_val) = 1; + break; + default: + fatal_with_file_and_line ("unrecognized flag: `%c'", flag_char); + } + } +} + +/* Remap a register number in a dump to an equivalent register number + for the current target. */ + +unsigned int +regno_remapper::get_effective_regno (int dumped_regno) const +{ + /* 0 means "do nothing". */ + if (m_dumped_first_pseudo_regno == 0) + return dumped_regno; + + if (dumped_regno < m_dumped_first_pseudo_regno) + /* Assume we have a reference to a physical or virtual regno. + Do not remap. */ + return dumped_regno; + + /* Otherwise, assume that DUMPED_REGNO was a pseudo reg. + Offset it to the correct range for pseudo regs for this target. */ + return (LAST_VIRTUAL_REGISTER + 1 + + (dumped_regno - m_dumped_first_pseudo_regno)); +} + +int +rtx_reader::remap_regno (int dumped_regno) const +{ + if (!m_policy) + return dumped_regno; + + if (!m_policy->m_regno_remapper) + return dumped_regno; + + return m_policy->m_regno_remapper->get_effective_regno (dumped_regno); +} + /* Subroutine of read_rtx and read_nested_rtx. CODE_NAME is the name of either an rtx code or a code iterator. Parse the rest of the rtx and return it. */ -static rtx +rtx read_rtx_code (const char *code_name) { int i; RTX_CODE code; - struct mapping *iterator, *m; + struct mapping *iterator = NULL, *m; const char *format_ptr; struct md_name name; rtx return_rtx; @@ -1108,13 +1334,19 @@ read_rtx_code (const char *code_name) rtx value; /* Value of this node. */ }; + one_time_initialization (); + /* If this code is an iterator, build the rtx using the iterator's first value. */ +#ifdef GENERATOR_FILE iterator = (struct mapping *) htab_find (codes.iterators, &code_name); if (iterator != 0) code = (enum rtx_code) iterator->values->number; else code = (enum rtx_code) codes.find_builtin (code_name); +#else + code = (enum rtx_code) codes.find_builtin (code_name); +#endif /* If we end up with an insn expression then we free this space below. */ return_rtx = rtx_alloc (code); @@ -1125,6 +1357,27 @@ read_rtx_code (const char *code_name) if (iterator) record_iterator_use (iterator, return_rtx); + /* Check for flags. */ + read_flags (return_rtx); + + /* Read REG_NOTE names for EXPR_LIST and INSN_LIST. */ +#ifndef GENERATOR_FILE + if (GET_CODE (return_rtx) == EXPR_LIST + || GET_CODE (return_rtx) == INSN_LIST + || GET_CODE (return_rtx) == INT_LIST) + { + char ch = read_char (); + if (ch == ':') + { + read_name (&name); + PUT_MODE_RAW (return_rtx, + (machine_mode)parse_reg_note_name (name.string)); + } + else + unread_char (ch); + } +#endif + /* If what follows is `: mode ', read it and store the mode in the rtx. */ @@ -1137,6 +1390,12 @@ read_rtx_code (const char *code_name) else unread_char (i); + if (INSN_CHAIN_CODE_P (code)) + { + read_name (&name); + INSN_UID (return_rtx) = atoi (name.string); + } + for (i = 0; format_ptr[i] != 0; i++) switch (format_ptr[i]) { @@ -1145,11 +1404,103 @@ read_rtx_code (const char *code_name) case '0': if (code == REG) ORIGINAL_REGNO (return_rtx) = REGNO (return_rtx); + else if (i == 1 && code == SYMBOL_REF) + { + /* Possibly wrote " [flags %#x]", SYMBOL_REF_FLAGS (in_rtx). */ + c = read_skip_spaces (); + if (c == '[') + { + file_location loc = read_name (&name); + if (strcmp (name.string, "flags")) + error_at (loc, "was expecting `%s'", "flags"); + read_name (&name); + SYMBOL_REF_FLAGS (return_rtx) = strtol (name.string, NULL, 16); + + /* We can't reconstruct SYMBOL_REF_BLOCK; set it to NULL. */ + if (SYMBOL_REF_HAS_BLOCK_INFO_P (return_rtx)) + SYMBOL_REF_BLOCK (return_rtx) = NULL; + + require_char (']'); + } + else + unread_char (c); + + /* Possibly wrote: + print_node_brief (outfile, "", SYMBOL_REF_DECL (in_rtx), + dump_flags); */ + c = read_skip_spaces (); + if (c == '<') + { + /* Skip the content for now. */ + while (1) + { + char ch = read_char (); + if (ch == '>') + break; + } + } + else + unread_char (c); + } + else if (i == 3 && code == NOTE) + { + /* Note-specific data appears for operand 3, which annoyingly + is before the enum specifying which kind of note we have + (operand 4). */ +#ifndef GENERATOR_FILE + c = read_skip_spaces (); + if (c == '[') + { + /* Possibly data for a NOTE_INSN_BASIC_BLOCK, of the form: + [bb %d]. */ + file_location bb_loc = read_name (&name); + if (strcmp (name.string, "bb")) + error_at (bb_loc, "was expecting `%s'", "bb"); + read_name (&name); + int bb_idx = atoi (name.string); + rtx_reader_ptr->add_fixup_note_insn_basic_block (bb_loc, + return_rtx, i, + bb_idx); + require_char_ws (']'); + } + else + unread_char (c); +#endif /* #ifndef GENERATOR_FILE */ + } + else if (i == 7 && JUMP_P (return_rtx)) + { +#ifndef GENERATOR_FILE + c = read_skip_spaces (); + if (c != '-') + { + unread_char (c); + break; + } + require_char ('>'); + file_location loc = read_name (&name); + rtx_reader_ptr->add_fixup_jump_label (loc, return_rtx, i, name.string); +#endif /* #ifndef GENERATOR_FILE */ + } break; case 'e': + XEXP (return_rtx, i) = read_nested_rtx (); + break; + case 'u': +#ifdef GENERATOR_FILE XEXP (return_rtx, i) = read_nested_rtx (); +#else + { + /* The RTL file recorded the ID of an insn (or 0 for NULL); we + must store this as a pointer, but the insn might not have + been loaded yet. Store the ID away for now. */ + file_location loc = read_name (&name); + int insn_id = atoi (name.string); + if (insn_id) + rtx_reader_ptr->add_fixup_insn_uid (loc, return_rtx, i, insn_id); + } +#endif break; case 'V': @@ -1223,7 +1574,10 @@ read_rtx_code (const char *code_name) star_if_braced = (format_ptr[i] == 'T'); stringbuf = read_string (star_if_braced); + if (!stringbuf) + break; +#ifdef GENERATOR_FILE /* For insn patterns, we want to provide a default name based on the file and line, like "*foo.md:12", if the given name is blank. These are only for define_insn and @@ -1246,6 +1600,7 @@ read_rtx_code (const char *code_name) obstack_grow (&string_obstack, line_name, strlen (line_name)+1); stringbuf = XOBFINISH (&string_obstack, char *); } +#endif /* #ifdef GENERATOR_FILE */ /* Find attr-names in the string. */ ptr = &tmpstr[0]; @@ -1275,10 +1630,21 @@ read_rtx_code (const char *code_name) record_iterator_use (m, return_rtx); } + /* "stringbuf" was allocated within string_obstack and thus has + the its lifetime restricted to that of the rtx_reader. This is + OK for the generator programs, but for non-generator programs, + XSTR and XTMPL fields are meant to be allocated in the GC-managed + heap. Hence we need to allocate a copy in the GC-managed heap + for the non-generator case. */ + const char *ptr = stringbuf; +#ifndef GENERATOR_FILE + ptr = ggc_strdup (stringbuf); +#endif /* #ifndef GENERATOR_FILE */ + if (star_if_braced) - XTMPL (return_rtx, i) = stringbuf; + XTMPL (return_rtx, i) = ptr; else - XSTR (return_rtx, i) = stringbuf; + XSTR (return_rtx, i) = ptr; } break; @@ -1301,27 +1667,251 @@ read_rtx_code (const char *code_name) #endif #endif XWINT (return_rtx, i) = tmp_wide; +#ifndef GENERATOR_FILE + /* Strip away the redundant hex dump of the value. */ + { + require_char_ws ('['); + read_name (&name); + require_char_ws (']'); + } +#endif break; case 'i': case 'n': +#ifdef GENERATOR_FILE /* Can be an iterator or an integer constant. */ read_name (&name); record_potential_iterator_use (&ints, &XINT (return_rtx, i), name.string); +#else + /* Handle some of the extra information that print_rtx + can write out for these cases. */ + { + /* print_rtx only writes out operand 5 for notes + for NOTE_KIND values NOTE_INSN_DELETED_LABEL + and NOTE_INSN_DELETED_DEBUG_LABEL. */ + if (i == 5 && NOTE_P (return_rtx)) + break; + + if (i == 4 && INSN_P (return_rtx)) + { + maybe_read_location (i, return_rtx); + break; + } + + read_name (&name); + int value; + if (format_ptr[i] == 'n') + value = parse_note_insn_name (name.string); + else + value = atoi (name.string); + XINT (return_rtx, i) = value; + + /* print-rtl.c prints the names of recognized insns, after + the insn code, wrapping them in braces. Skip them, + and reset the insn code to be unrecognized, since insn + codes are likely to change every time the .md files + change. */ + if (i == 5 && INSN_P (return_rtx) && format_ptr[i] == 'i') + if (value >= 0) + { + /* Reset the insn to be unrecognized. */ + INSN_CODE (return_rtx) = -1; + + /* Skip the braces and their content. */ + require_char_ws ('{'); + while (1) + { + char ch = read_char (); + if (ch == '}') + break; + } + } + } +#endif + break; + + case 'B': + { + file_location loc = read_name_or_nil (&name); + int bb_idx = atoi (name.string); + if (bb_idx) + rtx_reader_ptr->add_fixup_bb (loc, return_rtx, i, bb_idx); + } break; case 'r': - read_name (&name); - validate_const_int (name.string); - set_regno_raw (return_rtx, atoi (name.string), 1); - REG_ATTRS (return_rtx) = NULL; + { + read_name (&name); + validate_const_int (name.string); + + int dumped_regno = atoi (name.string); + int effective_regno = rtx_reader_ptr->remap_regno (dumped_regno); + set_regno_raw (return_rtx, effective_regno, 1); + + REG_ATTRS (return_rtx) = NULL; +#ifndef GENERATOR_FILE + unsigned int regno = REGNO (return_rtx); + ORIGINAL_REGNO (return_rtx) = regno; + + /* print_rtx can print a name for various registers + after the register number. Skip and discard it. */ + if (effective_regno < FIRST_PSEUDO_REGISTER + || effective_regno <= LAST_VIRTUAL_REGISTER) + read_name (&name); + + /* Parse extra stuff at end of 'r'. + We may have zero, one, or two sections marked by square + brackets. */ + int ch = read_skip_spaces (); + bool expect_original_regno = false; + if (ch == '[') + { + file_location loc = rtx_reader_ptr->get_current_location (); + char *desc = read_until ("]", true); + strip_trailing_whitespace (desc); + const char *desc_start = desc; + /* If ORIGINAL_REGNO (rtx) != regno, we will have: + "orig:%i", ORIGINAL_REGNO (rtx). + Consume it, we don't set ORIGINAL_REGNO, since we can + get that from the 2nd copy later. */ + if (0 == strncmp (desc, "orig:", 5)) + { + expect_original_regno = true; + desc_start += 5; + /* Skip to any whitespace following the integer. */ + const char *space = strchr (desc_start, ' '); + if (space) + desc_start = space + 1; + } + /* Any remaining text may be the REG_EXPR. Alternatively we have + no REG_ATTRS, and instead we have ORIGINAL_REGNO. */ + if (ISDIGIT (*desc_start)) + { + /* Assume we have ORIGINAL_REGNO. */ + ORIGINAL_REGNO (return_rtx) = atoi (desc_start); + } + else + { + /* Assume we have REG_EXPR. */ + rtx_reader_ptr->add_fixup_expr + (loc, consolidate_singletons (return_rtx), desc_start); + } + free (desc); + } + else + unread_char (ch); + if (expect_original_regno) + { + require_char_ws ('['); + char *desc = read_until ("]", true); + ORIGINAL_REGNO (return_rtx) = atoi (desc); + free (desc); + } +#endif + } break; default: gcc_unreachable (); } +#ifndef GENERATOR_FILE + /* Handle the various additional information that print-rtl.c can + write after the regular fields. */ + switch (GET_CODE (return_rtx)) + { + case MEM: + { + int ch; + require_char_ws ('['); + read_name (&name); + MEM_ALIAS_SET (return_rtx) = atoi (name.string); + /* We have either a MEM_EXPR, or a space. */ + if (peek_char () != ' ') + { + file_location loc = rtx_reader_ptr->get_current_location (); + char *desc = read_until (" +", false); + rtx_reader_ptr->add_fixup_expr + (loc, consolidate_singletons (return_rtx), desc); + free (desc); + } + else + read_char (); + + /* We may optionally have '+' for MEM_OFFSET_KNOWN_P. */ + ch = read_skip_spaces (); + if (ch == '+') + { + read_name (&name); + MEM_OFFSET_KNOWN_P (return_rtx) = 1; + MEM_OFFSET (return_rtx) = atoi (name.string); + } + else + unread_char (ch); + + /* Handle optional " S" for MEM_SIZE. */ + ch = read_skip_spaces (); + if (ch == 'S') + { + read_name (&name); + MEM_SIZE (return_rtx) = atoi (name.string); + } + else + unread_char (ch); + + /* Handle optional " A" for MEM_ALIGN. */ + ch = read_skip_spaces (); + if (ch == 'A' && peek_char () != 'S') + { + read_name (&name); + MEM_ALIGN (return_rtx) = atoi (name.string); + } + + /* Handle optional " AS" for MEM_ADDR_SPACE. */ + ch = read_skip_spaces (); + if (ch == 'A' && peek_char () == 'S') + { + read_char (); + read_name (&name); + MEM_ADDR_SPACE (return_rtx) = atoi (name.string); + } + else + unread_char (ch); + + require_char (']'); + } + break; + + case CODE_LABEL: + { + /* Parse LABEL_NUSES. */ + require_char_ws ('['); + read_name (&name); + LABEL_NUSES (return_rtx) = atoi (name.string); + require_word_ws ("uses"); + require_char_ws (']'); + /* TODO: parse LABEL_KIND. */ + /* For now, skip until closing ')'. */ + do + { + char ch = read_char (); + if (ch == ')') + { + unread_char (ch); + break; + } + } + while (1); + } + break; + + default: + break; + } +#endif + if (CONST_WIDE_INT_P (return_rtx)) { read_name (&name); @@ -1382,6 +1972,135 @@ read_rtx_code (const char *code_name) return return_rtx; } +#ifndef GENERATOR_FILE + +/* Helper function for consolidate_reg. */ + +static rtx +lookup_global_register (int regno) +{ + /* FIXME: do we need to check for Pmode? */ + /* FIXME: should we instead look for the reg name, and + remap the number accordingly? */ + /* We can't use a switch here, as some of the REGNUMs might not be constants + for some targets. */ + if (regno == STACK_POINTER_REGNUM) + return stack_pointer_rtx; + else if (regno == FRAME_POINTER_REGNUM) + return frame_pointer_rtx; + else if (regno == HARD_FRAME_POINTER_REGNUM) + return hard_frame_pointer_rtx; + else if (regno == ARG_POINTER_REGNUM) + return arg_pointer_rtx; + else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM) + return virtual_incoming_args_rtx; + else if (regno == VIRTUAL_STACK_VARS_REGNUM) + return virtual_stack_vars_rtx; + else if (regno == VIRTUAL_STACK_DYNAMIC_REGNUM) + return virtual_stack_dynamic_rtx; + else if (regno == VIRTUAL_OUTGOING_ARGS_REGNUM) + return virtual_outgoing_args_rtx; + else if (regno == VIRTUAL_CFA_REGNUM) + return virtual_cfa_rtx; + else if (regno == VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM) + return virtual_preferred_stack_boundary_rtx; +#ifdef return_ADDRESS_POINTER_REGNUM + else if (regno == RETURN_ADDRESS_POINTER_REGNUM) + return return_address_pointer_rtx; +#endif + + return NULL; +} + +/* Helper function for consolidate_singletons, for handling REG instances. */ + +static rtx +consolidate_reg (rtx x) +{ + gcc_assert (GET_CODE (x) == REG); + + int regno = REGNO (x); + + /* Normally REG instances are created by gen_reg_rtx which updates + regno_reg_rtx, growing it as necessary. + The REG instances created from the dumpfile weren't created this + way, so we need to manually update regno_reg_rtx. */ + if (regno >= crtl->emit.regno_pointer_align_length) + { + int old_size = crtl->emit.regno_pointer_align_length; + int new_size = MAX (regno + 1, old_size * 2); + char *tmp; + rtx *new1; + + tmp = XRESIZEVEC (char, crtl->emit.regno_pointer_align, new_size); + memset (tmp + old_size, 0, old_size); + crtl->emit.regno_pointer_align = (unsigned char *) tmp; + + new1 = GGC_RESIZEVEC (rtx, regno_reg_rtx, new_size); + memset (new1 + old_size, 0, (new_size - old_size) * sizeof (rtx)); + regno_reg_rtx = new1; + + crtl->emit.regno_pointer_align_length = new_size; + } + gcc_assert (regno < crtl->emit.regno_pointer_align_length); + + if (reg_rtx_no < regno + 1) + reg_rtx_no = regno + 1; + + /* Some register numbers have their rtx created in init_emit_regs + e.g. stack_pointer_rtx for STACK_POINTER_REGNUM. + Consolidate on this. */ + rtx global_reg = lookup_global_register (regno); + if (global_reg) + return global_reg; + + /* Populate regno_reg_rtx if necessary. */ + if (regno_reg_rtx[regno] == NULL) + regno_reg_rtx[regno] = x; + /* Use it. */ + gcc_assert (GET_CODE (regno_reg_rtx[regno]) == REG); + gcc_assert (REGNO (regno_reg_rtx[regno]) == regno); + if (GET_MODE (x) == GET_MODE (regno_reg_rtx[regno])) + return regno_reg_rtx[regno]; + + return x; +} + +/* When reading RTL function dumps, we must consolidate some + rtx so that we use singletons where singletons are expected + (e.g. we don't want multiple "(const_int 0 [0])" rtx, since + these are tested via pointer equality against const0_rtx. */ + +static rtx +consolidate_singletons (rtx x) +{ + if (!x) + return x; + + switch (GET_CODE (x)) + { + /* FIXME: do we need to check for VOIDmode for these? */ + case PC: return pc_rtx; + case RETURN: return ret_rtx; + case SIMPLE_RETURN: return simple_return_rtx; + case CC0: return cc0_rtx; + + case REG: + return consolidate_reg (x); + + case CONST_INT: + return gen_rtx_CONST_INT (GET_MODE (x), INTVAL (x)); + + default: + break; + } + + return x; +} + +#endif /* #ifndef GENERATOR_FILE */ + + /* Read a nested rtx construct from the MD file and return it. */ static rtx @@ -1400,6 +2119,10 @@ read_nested_rtx (void) require_char_ws (')'); +#ifndef GENERATOR_FILE + return_rtx = consolidate_singletons (return_rtx); +#endif /* #ifndef GENERATOR_FILE */ + return return_rtx; } diff --git a/gcc/rtl.h b/gcc/rtl.h index b531ab7..04f3e14 100644 --- a/gcc/rtl.h +++ b/gcc/rtl.h @@ -3641,7 +3641,10 @@ extern void init_varasm_once (void); extern rtx make_debug_expr_from_rtl (const_rtx); /* In read-rtl.c */ +#ifdef GENERATOR_FILE extern bool read_rtx (const char *, vec *); +#endif +extern rtx read_rtx_code (const char *code_name); /* In alias.c */ extern rtx canon_rtx (rtx); @@ -3746,5 +3749,6 @@ struct GTY(()) cgraph_rtl_info { unsigned function_used_regs_valid: 1; }; +extern rtx_insn *get_insn_by_uid (int uid); #endif /* ! GCC_RTL_H */ diff --git a/gcc/selftest-rtl.c b/gcc/selftest-rtl.c new file mode 100644 index 0000000..9c67b1a --- /dev/null +++ b/gcc/selftest-rtl.c @@ -0,0 +1,81 @@ +/* Selftest support for RTL. + Copyright (C) 2016 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "selftest.h" +#include "backend.h" +#include "target.h" +#include "rtl.h" +#include "read-rtl-function.h" +#include "read-md.h" +#include "tree-core.h" +#include "selftest-rtl.h" + +#if CHECKING_P + +namespace selftest { + +/* Constructor for selftest::rtl_dump_test. + Create tempfile containing DUMP_CONTENT, and read it. */ + +rtl_dump_test::rtl_dump_test (const char *dump_content, + int dumped_first_pseudo_regno) +: /* Write out DUMP_CONTENT to a tempfile. */ + m_tempfile (SELFTEST_LOCATION, ".rtl", dump_content), + + /* Remap registers >= dumped_first_pseudo_regno to be target-appropriate + pseudos, for the target's current value of LAST_VIRTUAL_REGISTER. */ + m_regno_remapper (dumped_first_pseudo_regno) +{ + rtl_reader_policy policy (&m_regno_remapper); + + /* Parse the tempfile. */ + auto_vec argv (2); + argv.safe_push (progname); + argv.safe_push (m_tempfile.get_filename ()); + bool read_ok + = read_rtl_function_body (argv.length (), argv.address (), NULL, &policy); + ASSERT_TRUE (read_ok); +} + +/* Destructor for selftest::rtl_dump_test. + Cleanup global state relating to the function. + Also, implicitly unlink the tempfile (via its dtor). */ + +selftest::rtl_dump_test::~rtl_dump_test () +{ + /* Cleanups. */ + current_function_decl = NULL; + free_after_compilation (cfun); + set_cfun (NULL); +} + +/* Given INPUT_REGNO, a register number in the input RTL dump, return + the effective register number that was used. */ +unsigned int +selftest::rtl_dump_test::effective_regno (int input_regno) const +{ + return m_regno_remapper.get_effective_regno (input_regno); +} + +} // namespace selftest + +#endif /* #if CHECKING_P */ diff --git a/gcc/selftest-rtl.h b/gcc/selftest-rtl.h new file mode 100644 index 0000000..a466925 --- /dev/null +++ b/gcc/selftest-rtl.h @@ -0,0 +1,66 @@ +/* A self-testing framework, for use by -fself-test. + Copyright (C) 2016 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef GCC_SELFTEST_RTL_H +#define GCC_SELFTEST_RTL_H + +/* The selftest code should entirely disappear in a production + configuration, hence we guard all of it with #if CHECKING_P. */ + +#if CHECKING_P + +#include "read-md.h" + +namespace selftest { + +/* A class for testing RTL function dumps. + Write DUMP_CONTENT to a tempfile and parse it as a function. + If non-zero, treat DUMPED_FIRST_PSEUDO_REGNO as the lowest-numbered + pseduoreg in the dump, remapping register numbers accordingly as + they are loaded. */ + +class rtl_dump_test +{ + public: + rtl_dump_test (const char *dump_content, int dumped_first_pseudo_regno = 0); + ~rtl_dump_test (); + + unsigned int effective_regno (int input_regno) const; + + private: + temp_source_file m_tempfile; + regno_remapper m_regno_remapper; +}; + +/* Wrapper class for initializing/cleaning up df. */ + +class dataflow_test +{ + public: + dataflow_test (); + ~dataflow_test (); +}; + +extern void verify_three_block_rtl_cfg (function *fun); + +} /* end of namespace selftest. */ + +#endif /* #if CHECKING_P */ + +#endif /* GCC_SELFTEST_RTL_H */ diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c index 54a9b0f..c90037c 100644 --- a/gcc/selftest-run-tests.c +++ b/gcc/selftest-run-tests.c @@ -63,6 +63,7 @@ selftest::run_tests () tree_c_tests (); gimple_c_tests (); rtl_tests_c_tests (); + read_rtl_function_c_tests (); /* Higher-level tests, or for components that other selftests don't rely on. */ diff --git a/gcc/selftest.h b/gcc/selftest.h index 86ad14c..75fea6f 100644 --- a/gcc/selftest.h +++ b/gcc/selftest.h @@ -203,6 +203,7 @@ extern void hash_map_tests_c_tests (); extern void hash_set_tests_c_tests (); extern void input_c_tests (); extern void pretty_print_c_tests (); +extern void read_rtl_function_c_tests (); extern void rtl_tests_c_tests (); extern void selftest_c_tests (); extern void spellcheck_c_tests (); diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c index 9a3b072..ec63b34 100644 --- a/gcc/tree-dfa.c +++ b/gcc/tree-dfa.c @@ -305,6 +305,11 @@ ssa_default_def (struct function *fn, tree var) gcc_assert (TREE_CODE (var) == VAR_DECL || TREE_CODE (var) == PARM_DECL || TREE_CODE (var) == RESULT_DECL); + + /* Always NULL_TREE for rtl function dumps. */ + if (!fn->gimple_df) + return NULL_TREE; + in.var = (tree)&ind; ind.uid = DECL_UID (var); return DEFAULT_DEFS (fn)->find_with_hash ((tree)&in, DECL_UID (var)); -- 1.8.5.3