This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[PATCH 8a/9] Introduce class function_reader (v6)
- From: David Malcolm <dmalcolm at redhat dot com>
- To: Bernd Schmidt <bschmidt at redhat dot com>, gcc-patches at gcc dot gnu dot org
- Cc: David Malcolm <dmalcolm at redhat dot com>
- Date: Thu, 1 Dec 2016 21:00:09 -0500
- Subject: [PATCH 8a/9] Introduce class function_reader (v6)
- Authentication-results: sourceware.org; auth=none
- References: <1480628601.24224.64.camel@redhat.com>
Changed in v6:
- split out dump-reading selftests into followup patches
(target-independent, and target-specific)
- removes unneeded headers from read-rtl-function.c
- numerous other cleanups identified in review
Changed in v5:
- updated for change to emit_status::ensure_regno_capacity
Changed in v4:
- error-handling changes split out into a separate patch
- rewritten the loader to use the new "compact" dump format
- support for reuse_rtx in loader
- handling of params, DECL_RTL and DECL_RTL_INCOMING
- moved target-dependent selftests to target-specific code
(aarch64.c and i386.c)
Link to earlier version of the patch:
https://gcc.gnu.org/ml/gcc-patches/2016-10/msg00267.html
gcc/ChangeLog:
* Makefile.in (OBJS): Add read-md.o, read-rtl.o,
read-rtl-function.o, and selftest-rtl.o.
* emit-rtl.c (maybe_set_max_label_num): New function.
* emit-rtl.h (maybe_set_max_label_num): New decl.
* function.c (instantiate_decls): Guard call to
instantiate_decls_1 with if (DECL_INITIAL (fndecl)).
* gensupport.c (gen_reader::gen_reader): Pass "false"
for new "compact" param of rtx_reader.
* print-rtl.c (rtx_writer::print_rtx_operand): Print "(nil)"
rather than an empty string for NULL strings.
* read-md.c: Potentially include config.h rather than bconfig.h.
(md_reader::read_name): Rename to...
(md_reader::read_name_1): ...this, adding "out_loc" param,
and converting "missing name or number" to returning false, rather
than failing.
(md_reader::read_name): Reimplement in terms of read_name_1.
(md_reader::read_name_or_nil): New function.
(md_reader::read_string): Handle "(nil)" by returning NULL.
(md_reader::md_reader): Add new param "compact".
* read-md.h (md_reader::md_reader): Add new param "compact".
(md_reader::is_compact): New accessor.
(md_reader::read_name): Convert return type from void to
file_location.
(md_reader::read_name_or_nil): New decl.
(md_reader::read_name_1): New decl.
(md_reader::m_compact): New field.
(noop_reader::noop_reader): Pass "false" for new "compact" param
of rtx_reader.
(rtx_reader::rtx_reader): Add new "compact" param.
(rtx_reader::read_rtx_operand): Make virtual and convert return
type from void to rtx.
(rtx_reader::read_until): New decl.
(rtx_reader::handle_any_trailing_information): New virtual
function.
(rtx_reader::postprocess): New virtual function.
(rtx_reader::finalize_string): New virtual function.
(rtx_reader::m_in_call_function_usage): New field.
(rtx_reader::m_reuse_rtx_by_id): New field.
* read-rtl-function.c: New file.
* read-rtl-function.h: New file.
* read-rtl.c: Potentially include config.h rather than bconfig.h.
For host, include function.h, memmodel.h, and emit-rtl.h.
(one_time_initialization): New function.
(struct compact_insn_name): New struct.
(compact_insn_names): New array.
(find_code): Handle insn codes in compact dumps.
(apply_subst_iterator): Wrap with #ifdef GENERATOR_FILE.
(bind_subst_iter_and_attr): Likewise.
(add_condition_to_string): Likewise.
(add_condition_to_rtx): Likewise.
(apply_attribute_uses): Likewise.
(add_current_iterators): Likewise.
(apply_iterators): Likewise.
(initialize_iterators): Guard usage of apply_subst_iterator with
#ifdef GENERATOR_FILE.
(read_conditions): Wrap with #ifdef GENERATOR_FILE.
(md_reader::read_mapping): Likewise.
(add_define_attr_for_define_subst): Likewise.
(add_define_subst_attr): Likewise.
(read_subst_mapping): Likewise.
(check_code_iterator): Likewise.
(rtx_reader::read_rtx): Likewise. Move one-time initialization
logic to...
(one_time_initialization): New function.
(rtx_reader::read_until): New method.
(read_flags): New function.
(parse_reg_note_name): New function.
(rtx_reader::read_rtx_code): Initialize "iterator" to NULL.
Call one_time_initialization. Handle reuse_rtx ids.
Wrap iterator lookup within #ifdef GENERATOR_FILE.
Add parsing support for RTL dumps, mirroring the special-cases in
print_rtx, by calling read_flags, reading REG_NOTE names, INSN_UID
values, and calling handle_any_trailing_information.
(rtx_reader::read_rtx_operand): Convert return type from void
to rtx, returning return_rtx. Handle case 'e'. Call
finalize_string on XSTR and XTMPL fields.
(rtx_reader::read_nested_rtx): Handle dumps in which trailing
"(nil)" values were omitted. Call the postprocess vfunc on the
return_rtx.
(rtx_reader::rtx_reader): Add new "compact" param and pass to base
class ctor. Initialize m_in_call_function_usage.
* rtl-tests.c (selftest::test_uncond_jump): Call
set_new_first_and_last_insn.
* rtl.h (read_rtx): Wrap decl with #ifdef GENERATOR_FILE.
* selftest-rtl.c: New file.
* selftest-rtl.h (class selftest::rtl_dump_test): New class.
(selftest::get_insn_by_uid): New decl.
* selftest-run-tests.c (selftest::run_tests): Call
read_rtl_function_c_tests.
* selftest.h (selftest::read_rtl_function_c_tests): New decl.
* tree-dfa.c (ssa_default_def): Return NULL_TREE for rtl function
dumps.
---
gcc/Makefile.in | 4 +
gcc/emit-rtl.c | 13 +
gcc/emit-rtl.h | 2 +
gcc/function.c | 3 +-
gcc/gensupport.c | 2 +-
gcc/print-rtl.c | 2 +-
gcc/read-md.c | 59 +-
gcc/read-md.h | 30 +-
gcc/read-rtl-function.c | 1657 ++++++++++++++++++++++++++++++++++++++++++++++
gcc/read-rtl-function.h | 26 +
gcc/read-rtl.c | 271 +++++++-
gcc/rtl-tests.c | 1 +
gcc/rtl.h | 2 +
gcc/selftest-rtl.c | 82 +++
gcc/selftest-rtl.h | 17 +
gcc/selftest-run-tests.c | 1 +
gcc/selftest.h | 1 +
gcc/tree-dfa.c | 5 +
18 files changed, 2148 insertions(+), 30 deletions(-)
create mode 100644 gcc/read-rtl-function.c
create mode 100644 gcc/read-rtl-function.h
create mode 100644 gcc/selftest-rtl.c
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index c265893..73d12dc 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1407,6 +1407,9 @@ OBJS = \
print-rtl-function.o \
print-tree.o \
profile.o \
+ read-md.o \
+ read-rtl.o \
+ read-rtl-function.o \
real.o \
realmpfr.o \
recog.o \
@@ -1434,6 +1437,7 @@ OBJS = \
sel-sched-ir.o \
sel-sched-dump.o \
sel-sched.o \
+ selftest-rtl.o \
selftest-run-tests.o \
sese.o \
shrink-wrap.o \
diff --git a/gcc/emit-rtl.c b/gcc/emit-rtl.c
index b2b5fde..6d64ad6 100644
--- a/gcc/emit-rtl.c
+++ b/gcc/emit-rtl.c
@@ -1374,6 +1374,19 @@ maybe_set_first_label_num (rtx_code_label *x)
if (CODE_LABEL_NUMBER (x) < first_label_num)
first_label_num = CODE_LABEL_NUMBER (x);
}
+
+/* For use by the RTL function loader, when mingling with normal
+ functions.
+ Ensure that label_num is greater than the label num of X, to avoid
+ duplicate labels in the generated assembler. */
+
+void
+maybe_set_max_label_num (rtx_code_label *x)
+{
+ if (CODE_LABEL_NUMBER (x) >= label_num)
+ label_num = CODE_LABEL_NUMBER (x) + 1;
+}
+
/* Return a value representing some low-order bits of X, where the number
of low-order bits is given by MODE. Note that no conversion is done
diff --git a/gcc/emit-rtl.h b/gcc/emit-rtl.h
index 21c180b..01f16a7 100644
--- a/gcc/emit-rtl.h
+++ b/gcc/emit-rtl.h
@@ -507,4 +507,6 @@ extern int get_mem_align_offset (rtx, unsigned int);
MODE and adjusted by OFFSET. */
extern rtx widen_memory_access (rtx, machine_mode, HOST_WIDE_INT);
+extern void maybe_set_max_label_num (rtx_code_label *x);
+
#endif /* GCC_EMIT_RTL_H */
diff --git a/gcc/function.c b/gcc/function.c
index 0b1d168..2674321 100644
--- a/gcc/function.c
+++ b/gcc/function.c
@@ -1901,7 +1901,8 @@ instantiate_decls (tree fndecl)
instantiate_decl_rtl (DECL_RTL (DECL_VALUE_EXPR (decl)));
/* Now process all variables defined in the function or its subblocks. */
- instantiate_decls_1 (DECL_INITIAL (fndecl));
+ if (DECL_INITIAL (fndecl))
+ instantiate_decls_1 (DECL_INITIAL (fndecl));
FOR_EACH_LOCAL_DECL (cfun, ix, decl)
if (DECL_RTL_SET_P (decl))
diff --git a/gcc/gensupport.c b/gcc/gensupport.c
index c49ad6f..64378e3 100644
--- a/gcc/gensupport.c
+++ b/gcc/gensupport.c
@@ -2233,7 +2233,7 @@ process_define_subst (void)
class gen_reader : public rtx_reader
{
public:
- gen_reader () : rtx_reader () {}
+ gen_reader () : rtx_reader (false) {}
void handle_unknown_directive (file_location, const char *);
};
diff --git a/gcc/print-rtl.c b/gcc/print-rtl.c
index 3bbd395..77e6b05 100644
--- a/gcc/print-rtl.c
+++ b/gcc/print-rtl.c
@@ -577,7 +577,7 @@ rtx_writer::print_rtx_operand (const_rtx in_rtx, int idx)
string:
if (str == 0)
- fputs (" \"\"", m_outfile);
+ fputs (" (nil)", m_outfile);
else
fprintf (m_outfile, " (\"%s\")", str);
m_sawclose = 1;
diff --git a/gcc/read-md.c b/gcc/read-md.c
index 6d9a1bd..a8462a6 100644
--- a/gcc/read-md.c
+++ b/gcc/read-md.c
@@ -17,7 +17,13 @@ You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+/* This file is compiled twice: once for the generator programs
+ once for the compiler. */
+#ifdef GENERATOR_FILE
#include "bconfig.h"
+#else
+#include "config.h"
+#endif
#include "system.h"
#include "coretypes.h"
#include "errors.h"
@@ -424,8 +430,8 @@ md_reader::peek_char (void)
/* Read an rtx code name into NAME. It is terminated by any of the
punctuation chars of rtx printed syntax. */
-void
-md_reader::read_name (struct md_name *name)
+bool
+md_reader::read_name_1 (struct md_name *name, file_location *out_loc)
{
int c;
size_t i;
@@ -463,8 +469,12 @@ md_reader::read_name (struct md_name *name)
c = read_char ();
}
+ unread_char (c);
+ *out_loc = get_current_location ();
+ read_char ();
+
if (i == 0)
- fatal_with_file_and_line ("missing name or number");
+ return false;
name->buffer[i] = 0;
name->string = name->buffer;
@@ -485,6 +495,36 @@ md_reader::read_name (struct md_name *name)
}
while (def);
}
+
+ return true;
+}
+
+/* Read an rtx code name into NAME. It is terminated by any of the
+ punctuation chars of rtx printed syntax. */
+
+file_location
+md_reader::read_name (struct md_name *name)
+{
+ file_location loc;
+ if (!read_name_1 (name, &loc))
+ fatal_with_file_and_line ("missing name or number");
+ return loc;
+}
+
+file_location
+md_reader::read_name_or_nil (struct md_name *name)
+{
+ file_location loc;
+ if (!read_name_1 (name, &loc))
+ {
+ file_location loc = get_current_location ();
+ read_skip_construct (0, loc);
+ /* Skip the ')'. */
+ read_char ();
+ name->buffer[0] = 0;
+ name->string = name->buffer;
+ }
+ return loc;
}
/* Subroutine of the string readers. Handles backslash escapes.
@@ -630,6 +670,14 @@ md_reader::read_string (int star_if_braced)
obstack_1grow (&m_string_obstack, '*');
stringbuf = read_braced_string ();
}
+ else if (saw_paren && c == 'n')
+ {
+ /* Handle (nil) by returning NULL. */
+ require_char ('i');
+ require_char ('l');
+ require_char_ws (')');
+ return NULL;
+ }
else
fatal_with_file_and_line ("expected `\"' or `{', found `%c'", c);
@@ -924,8 +972,9 @@ md_reader::traverse_enum_types (htab_trav callback, void *info)
/* Constructor for md_reader. */
-md_reader::md_reader ()
-: m_toplevel_fname (NULL),
+md_reader::md_reader (bool compact)
+: m_compact (compact),
+ m_toplevel_fname (NULL),
m_base_dir (NULL),
m_read_md_file (NULL),
m_read_md_filename (NULL),
diff --git a/gcc/read-md.h b/gcc/read-md.h
index 8910b75..06dc80e 100644
--- a/gcc/read-md.h
+++ b/gcc/read-md.h
@@ -94,7 +94,7 @@ struct enum_type {
class md_reader
{
public:
- md_reader ();
+ md_reader (bool compact);
virtual ~md_reader ();
bool read_md_files (int, const char **, bool (*) (const char *));
@@ -107,10 +107,13 @@ class md_reader
file_location get_current_location () const;
+ bool is_compact () const { return m_compact; }
+
/* Defined in read-md.c. */
int read_char (void);
void unread_char (int ch);
- void read_name (struct md_name *name);
+ file_location read_name (struct md_name *name);
+ file_location read_name_or_nil (struct md_name *);
void read_escape ();
char *read_quoted_string ();
char *read_braced_string ();
@@ -167,7 +170,12 @@ class md_reader
void handle_include (file_location loc);
void add_include_path (const char *arg);
+ bool read_name_1 (struct md_name *name, file_location *out_loc);
+
private:
+ /* Are we reading a compact dump? */
+ bool m_compact;
+
/* The name of the toplevel file that indirectly included
m_read_md_file. */
const char *m_toplevel_fname;
@@ -235,7 +243,7 @@ extern md_reader *md_reader_ptr;
class noop_reader : public md_reader
{
public:
- noop_reader () : md_reader () {}
+ noop_reader () : md_reader (false) {}
/* A dummy implementation which skips unknown directives. */
void handle_unknown_directive (file_location, const char *);
@@ -249,14 +257,26 @@ class noop_reader : public md_reader
class rtx_reader : public md_reader
{
public:
- rtx_reader ();
+ rtx_reader (bool compact);
~rtx_reader ();
bool read_rtx (const char *rtx_name, vec<rtx> *rtxen);
rtx read_rtx_code (const char *code_name);
- void read_rtx_operand (rtx return_rtx, int idx);
+ virtual rtx read_rtx_operand (rtx return_rtx, int idx);
rtx read_nested_rtx ();
rtx read_rtx_variadic (rtx form);
+ char *read_until (const char *terminator_chars, bool consume_terminator);
+
+ virtual void handle_any_trailing_information (rtx) {}
+ virtual rtx postprocess (rtx x) { return x; }
+ virtual const char *finalize_string (char *stringbuf) { return stringbuf; }
+
+ protected:
+ /* Analogous to rtx_writer's m_in_call_function_usage. */
+ bool m_in_call_function_usage;
+
+ /* Support for "reuse_rtx" directives. */
+ auto_vec<rtx> m_reuse_rtx_by_id;
};
/* Global singleton; constrast with md_reader_ptr above. */
diff --git a/gcc/read-rtl-function.c b/gcc/read-rtl-function.c
new file mode 100644
index 0000000..cef834e
--- /dev/null
+++ b/gcc/read-rtl-function.c
@@ -0,0 +1,1657 @@
+/* read-rtl-function.c - Reader for RTL function dumps
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "tree.h"
+#include "diagnostic.h"
+#include "read-md.h"
+#include "rtl.h"
+#include "cfghooks.h"
+#include "stringpool.h"
+#include "function.h"
+#include "tree-cfg.h"
+#include "cfg.h"
+#include "basic-block.h"
+#include "cfgrtl.h"
+#include "memmodel.h"
+#include "emit-rtl.h"
+#include "cgraph.h"
+#include "tree-pass.h"
+#include "toplev.h"
+#include "varasm.h"
+#include "read-rtl-function.h"
+#include "selftest.h"
+#include "selftest-rtl.h"
+
+/* Forward decls. */
+class function_reader;
+class fixup;
+
+/* Edges are recorded when parsing the "insn-chain" directive,
+ and created at the end when all the blocks ought to exist.
+ This struct records an "edge-from" or "edge-to" directive seen
+ at LOC, which will be turned into an actual CFG edge once
+ the "insn-chain" is fully parsed. */
+
+struct deferred_edge
+{
+ deferred_edge (file_location loc, int src_bb_idx, int dest_bb_idx, int flags)
+ : m_loc (loc), m_src_bb_idx (src_bb_idx), m_dest_bb_idx (dest_bb_idx),
+ m_flags (flags)
+ {}
+
+ file_location m_loc;
+ int m_src_bb_idx;
+ int m_dest_bb_idx;
+ int m_flags;
+};
+
+/* Subclass of rtx_reader for reading function dumps. */
+
+class function_reader : public rtx_reader
+{
+ public:
+ function_reader ();
+ ~function_reader ();
+
+ /* Overridden vfuncs of class md_reader. */
+ void handle_unknown_directive (file_location, const char *) FINAL OVERRIDE;
+
+ /* Overridden vfuncs of class rtx_reader. */
+ rtx read_rtx_operand (rtx x, int idx) FINAL OVERRIDE;
+ void handle_any_trailing_information (rtx x) FINAL OVERRIDE;
+ rtx postprocess (rtx) FINAL OVERRIDE;
+ const char *finalize_string (char *stringbuf) FINAL OVERRIDE;
+
+ rtx_insn **get_insn_by_uid (int uid);
+ tree parse_mem_expr (const char *desc);
+
+ private:
+ void parse_function ();
+ void create_function ();
+ void parse_param ();
+ void parse_insn_chain ();
+ void parse_block ();
+ int parse_bb_idx ();
+ void parse_edge (basic_block block, bool from);
+ rtx_insn *parse_insn (file_location loc, const char *name);
+ void parse_cfg (file_location loc);
+ void parse_crtl (file_location loc);
+ void create_edges ();
+
+ int parse_enum_value (int num_values, const char *const *strings);
+
+ void read_rtx_operand_u (rtx x, int idx);
+ void read_rtx_operand_i_or_n (rtx x, int idx, char format_char);
+ rtx read_rtx_operand_r (rtx x);
+ void extra_parsing_for_operand_code_0 (rtx x, int idx);
+
+ void add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx,
+ int insn_uid);
+
+ void add_fixup_note_insn_basic_block (file_location loc, rtx insn,
+ int operand_idx, int bb_idx);
+
+ void add_fixup_source_location (file_location loc, rtx insn,
+ int operand_idx,
+ const char *filename, int lineno);
+
+ void add_fixup_expr (file_location loc, rtx x,
+ const char *desc);
+
+ rtx consolidate_singletons (rtx x);
+ rtx parse_rtx ();
+ void maybe_read_location (int operand_idx, rtx insn);
+
+ void handle_insn_uids ();
+ void apply_fixups ();
+
+ private:
+ struct uid_hash : int_hash <int, -1, -2> {};
+ hash_map<uid_hash, rtx_insn *> m_insns_by_uid;
+ auto_vec<fixup *> m_fixups;
+ rtx_insn *m_first_insn;
+ auto_vec<tree> m_fake_scope;
+ char *m_name;
+ bool m_have_crtl_directive;
+ basic_block m_bb_to_insert_after;
+ auto_vec <deferred_edge> m_deferred_edges;
+ int m_highest_bb_idx;
+};
+
+/* Abstract base class for recording post-processing steps that must be
+ done after reading a .rtl file. */
+
+class fixup
+{
+ public:
+ fixup (file_location loc, rtx x)
+ : m_loc (loc), m_rtx (x)
+ {}
+ virtual ~fixup () {}
+
+ virtual void apply (function_reader *reader) const = 0;
+
+ protected:
+ file_location m_loc;
+ rtx m_rtx;
+};
+
+/* An abstract subclass of fixup for post-processing steps that
+ act on a specific operand of a specific instruction. */
+
+class operand_fixup : public fixup
+{
+ public:
+ operand_fixup (file_location loc, rtx insn, int operand_idx)
+ : fixup (loc, insn), m_operand_idx (operand_idx)
+ {}
+
+ protected:
+ int m_operand_idx;
+};
+
+/* A concrete subclass of operand_fixup: fixup an rtx_insn *
+ field (NEXT_INSN/PREV_INSN) based on an integer UID. */
+
+class fixup_insn_uid : public operand_fixup
+{
+ public:
+ fixup_insn_uid (file_location loc, rtx insn, int operand_idx, int insn_uid)
+ : operand_fixup (loc, insn, operand_idx),
+ m_insn_uid (insn_uid)
+ {}
+
+ void apply (function_reader *reader) const;
+
+ private:
+ int m_insn_uid;
+};
+
+/* A concrete subclass of operand_fixup: fix up a
+ NOTE_INSN_BASIC_BLOCK based on an integer block ID. */
+
+class fixup_note_insn_basic_block : public operand_fixup
+{
+ public:
+ fixup_note_insn_basic_block (file_location loc, rtx insn, int operand_idx,
+ int bb_idx)
+ : operand_fixup (loc, insn, operand_idx),
+ m_bb_idx (bb_idx)
+ {}
+
+ void apply (function_reader *reader) const;
+
+ private:
+ int m_bb_idx;
+};
+
+/* A concrete subclass of fixup (not operand_fixup): fix up
+ the expr of an rtx (REG or MEM) based on a textual dump. */
+
+class fixup_expr : public fixup
+{
+ public:
+ fixup_expr (file_location loc, rtx x, const char *desc)
+ : fixup (loc, x),
+ m_desc (xstrdup (desc))
+ {}
+
+ ~fixup_expr () { free (m_desc); }
+
+ void apply (function_reader *reader) const;
+
+ private:
+ char *m_desc;
+};
+
+/* Return a textual description of the given operand of the given rtx. */
+
+static const char *
+get_operand_name (rtx insn, int operand_idx)
+{
+ gcc_assert (is_a <rtx_insn *> (insn));
+ switch (operand_idx)
+ {
+ case 0:
+ return "PREV_INSN";
+ case 1:
+ return "NEXT_INSN";
+ default:
+ return NULL;
+ }
+}
+
+/* Fixup an rtx_insn * field (NEXT_INSN/PREV_INSN) based on an integer
+ UID. */
+
+void
+fixup_insn_uid::apply (function_reader *reader) const
+{
+ rtx_insn **insn_from_uid = reader->get_insn_by_uid (m_insn_uid);
+ if (insn_from_uid)
+ XEXP (m_rtx, m_operand_idx) = *insn_from_uid;
+ else
+ {
+ const char *op_name = get_operand_name (m_rtx, m_operand_idx);
+ if (op_name)
+ error_at (m_loc,
+ "insn with UID %i not found for operand %i (`%s') of insn %i",
+ m_insn_uid, m_operand_idx, op_name, INSN_UID (m_rtx));
+ else
+ error_at (m_loc,
+ "insn with UID %i not found for operand %i of insn %i",
+ m_insn_uid, m_operand_idx, INSN_UID (m_rtx));
+ }
+}
+
+/* Fix up a NOTE_INSN_BASIC_BLOCK based on an integer block ID. */
+
+void
+fixup_note_insn_basic_block::apply (function_reader *) const
+{
+ basic_block bb = BASIC_BLOCK_FOR_FN (cfun, m_bb_idx);
+ gcc_assert (bb);
+ NOTE_BASIC_BLOCK (m_rtx) = bb;
+}
+
+/* Fix up the expr of an rtx (REG or MEM) based on a textual dump. */
+
+void
+fixup_expr::apply (function_reader *reader) const
+{
+ tree expr = reader->parse_mem_expr (m_desc);
+ switch (GET_CODE (m_rtx))
+ {
+ case REG:
+ set_reg_attrs_for_decl_rtl (expr, m_rtx);
+ break;
+ case MEM:
+ set_mem_expr (m_rtx, expr);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Strip trailing whitespace from DESC. */
+
+static void
+strip_trailing_whitespace (char *desc)
+{
+ char *terminator = desc + strlen (desc);
+ while (desc < terminator)
+ {
+ terminator--;
+ if (ISSPACE (*terminator))
+ *terminator = '\0';
+ else
+ break;
+ }
+}
+
+/* Return the numeric value n for GET_NOTE_INSN_NAME (n) for STRING,
+ or fail if STRING isn't recognized. */
+
+static int
+parse_note_insn_name (const char *string)
+{
+ for (int i = 0; i < NOTE_INSN_MAX; i++)
+ if (0 == strcmp (string, GET_NOTE_INSN_NAME (i)))
+ return i;
+ fatal_with_file_and_line ("unrecognized NOTE_INSN name: `%s'", string);
+}
+
+/* Return the register number for NAME, or return -1 if it isn't
+ recognized. */
+
+static int
+lookup_reg_by_dump_name (const char *name)
+{
+ for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ if (reg_names[i][0]
+ && ! strcmp (name, reg_names[i]))
+ return i;
+
+ /* Also lookup virtuals. */
+ if (!strcmp (name, "virtual-incoming-args"))
+ return VIRTUAL_INCOMING_ARGS_REGNUM;
+ if (!strcmp (name, "virtual-stack-vars"))
+ return VIRTUAL_STACK_VARS_REGNUM;
+ if (!strcmp (name, "virtual-stack-dynamic"))
+ return VIRTUAL_STACK_DYNAMIC_REGNUM;
+ if (!strcmp (name, "virtual-outgoing-args"))
+ return VIRTUAL_OUTGOING_ARGS_REGNUM;
+ if (!strcmp (name, "virtual-cfa"))
+ return VIRTUAL_CFA_REGNUM;
+ if (!strcmp (name, "virtual-preferred-stack-boundary"))
+ return VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM;
+ /* TODO: handle "virtual-reg-%d". */
+
+ /* In compact mode, pseudos are printed with a '%' sigil following
+ by the regno, offset by (LAST_VIRTUAL_REGISTER + 1), so that the
+ first non-virtual pseudo is dumped as "%0". */
+ if (name[0] == '%')
+ {
+ int dump_num = atoi (name + 1);
+ return dump_num + LAST_VIRTUAL_REGISTER + 1;
+ }
+
+ /* Not found. */
+ return -1;
+}
+
+/* class function_reader : public rtx_reader */
+
+/* function_reader's constructor. */
+
+function_reader::function_reader ()
+: rtx_reader (true),
+ m_first_insn (NULL),
+ m_name (NULL),
+ m_have_crtl_directive (false),
+ m_bb_to_insert_after (NULL),
+ m_highest_bb_idx (EXIT_BLOCK)
+{
+}
+
+/* function_reader's destructor. */
+
+function_reader::~function_reader ()
+{
+ int i;
+ fixup *f;
+ FOR_EACH_VEC_ELT (m_fixups, i, f)
+ delete f;
+
+ free (m_name);
+}
+
+/* Implementation of rtx_reader::handle_unknown_directive.
+
+ Require a top-level "function" directive, as emitted by
+ print_rtx_function, and parse it. */
+
+void
+function_reader::handle_unknown_directive (file_location start_loc,
+ const char *name)
+{
+ if (strcmp (name, "function"))
+ fatal_at (start_loc, "expected 'function'");
+
+ parse_function ();
+}
+
+/* Parse the output of print_rtx_function (or hand-written data in the
+ same format), having already parsed the "(function" heading, and
+ finishing immediately before the final ")".
+
+ The "param" and "crtl" clauses are optional. */
+
+void
+function_reader::parse_function ()
+{
+ m_name = xstrdup (read_string (0));
+
+ create_function ();
+
+ while (1)
+ {
+ int c = read_skip_spaces ();
+ if (c == ')')
+ {
+ unread_char (c);
+ break;
+ }
+ unread_char (c);
+ require_char ('(');
+ file_location loc = get_current_location ();
+ struct md_name directive;
+ read_name (&directive);
+ if (strcmp (directive.string, "param") == 0)
+ parse_param ();
+ else if (strcmp (directive.string, "insn-chain") == 0)
+ parse_insn_chain ();
+ else if (strcmp (directive.string, "crtl") == 0)
+ parse_crtl (loc);
+ else
+ fatal_with_file_and_line ("unrecognized directive: %s",
+ directive.string);
+ }
+
+ handle_insn_uids ();
+
+ apply_fixups ();
+
+ /* Rebuild the JUMP_LABEL field of any JUMP_INSNs in the chain, and the
+ LABEL_NUSES of any CODE_LABELs.
+
+ This has to happen after apply_fixups, since only after then do
+ LABEL_REFs have their label_ref_label set up. */
+ rebuild_jump_labels (get_insns ());
+
+ crtl->init_stack_alignment ();
+}
+
+/* Set up state for the function *before* fixups are applied.
+
+ Create "cfun" and a decl for the function.
+ By default, every function decl is hardcoded as
+ int test_1 (int i, int j, int k);
+ Set up various other state:
+ - the cfg and basic blocks (edges are created later, *after* fixups
+ are applied).
+ - add the function to the callgraph. */
+
+void
+function_reader::create_function ()
+{
+ /* We start in cfgrtl mode, rather than cfglayout mode. */
+ rtl_register_cfg_hooks ();
+
+ /* Create cfun. */
+ tree fn_name = get_identifier (m_name ? m_name : "test_1");
+ tree int_type = integer_type_node;
+ tree return_type = int_type;
+ tree arg_types[3] = {int_type, int_type, int_type};
+ tree fn_type = build_function_type_array (return_type, 3, arg_types);
+ tree fndecl = build_decl_stat (UNKNOWN_LOCATION, FUNCTION_DECL, fn_name,
+ fn_type);
+ tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
+ return_type);
+ DECL_ARTIFICIAL (resdecl) = 1;
+ DECL_IGNORED_P (resdecl) = 1;
+ DECL_RESULT (fndecl) = resdecl;
+ allocate_struct_function (fndecl, false);
+ /* This sets cfun. */
+
+ current_function_decl = fndecl;
+
+ cfun->curr_properties = (PROP_cfg | PROP_rtl);
+
+ /* Do we need this to force cgraphunit.c to output the function? */
+ DECL_EXTERNAL (fndecl) = 0;
+ DECL_PRESERVE_P (fndecl) = 1;
+
+ /* Add to cgraph. */
+ cgraph_node::finalize_function (fndecl, false);
+
+ /* Create bare-bones cfg. This creates the entry and exit blocks. */
+ init_empty_tree_cfg_for_function (cfun);
+ ENTRY_BLOCK_PTR_FOR_FN (cfun)->flags |= BB_RTL;
+ EXIT_BLOCK_PTR_FOR_FN (cfun)->flags |= BB_RTL;
+ init_rtl_bb_info (ENTRY_BLOCK_PTR_FOR_FN (cfun));
+ init_rtl_bb_info (EXIT_BLOCK_PTR_FOR_FN (cfun));
+ m_bb_to_insert_after = ENTRY_BLOCK_PTR_FOR_FN (cfun);
+
+}
+
+/* Parse the content of a "param" directive, having already parsed the
+ "(param". Consume the trailing ')'. */
+
+void
+function_reader::parse_param ()
+{
+ require_char_ws ('"');
+ char *name = read_quoted_string ();
+
+ /* Lookup param by name. */
+ tree t_param = parse_mem_expr (name);
+ // TODO: what if not found?
+
+ /* Parse DECL_RTL. */
+ require_char_ws ('(');
+ require_word_ws ("DECL_RTL");
+ DECL_WRTL_CHECK (t_param)->decl_with_rtl.rtl = parse_rtx ();
+ require_char_ws (')');
+
+ /* Parse DECL_RTL_INCOMING. */
+ require_char_ws ('(');
+ require_word_ws ("DECL_RTL_INCOMING");
+ DECL_INCOMING_RTL (t_param) = parse_rtx ();
+ require_char_ws (')');
+
+ require_char_ws (')');
+}
+
+/* Parse zero or more child insn elements within an
+ "insn-chain" element. Consume the trailing ')'. */
+
+void
+function_reader::parse_insn_chain ()
+{
+ while (1)
+ {
+ int c = read_skip_spaces ();
+ file_location loc = get_current_location ();
+ if (c == ')')
+ break;
+ else if (c == '(')
+ {
+ struct md_name directive;
+ read_name (&directive);
+ if (strcmp (directive.string, "block") == 0)
+ parse_block ();
+ else
+ parse_insn (loc, directive.string);
+ }
+ else
+ fatal_at (loc, "expected '(' or ')'");
+ }
+
+ create_edges ();
+}
+
+/* Parse zero or more child directives (edges and insns) within a
+ "block" directive, having already parsed the "(block " heading.
+ Consume the trailing ')'. */
+
+void
+function_reader::parse_block ()
+{
+ /* Parse the index value from the dump. This will be an integer;
+ we don't support "entry" or "exit" here (unlike for edges). */
+ struct md_name name;
+ read_name (&name);
+ int bb_idx = atoi (name.string);
+
+ /* The term "index" has two meanings for basic blocks in a CFG:
+ (a) the "index" field within struct basic_block_def.
+ (b) the index of a basic_block within the cfg's x_basic_block_info
+ vector, as accessed via BASIC_BLOCK_FOR_FN.
+
+ These can get out-of-sync when basic blocks are optimized away.
+ They get back in sync by "compact_blocks".
+ We reconstruct cfun->cfg->x_basic_block_info->m_vecdata with NULL
+ values in it for any missing basic blocks, so that (a) == (b) for
+ all of the blocks we create. The doubly-linked list of basic
+ blocks (next_bb/prev_bb) skips over these "holes". */
+
+ if (m_highest_bb_idx < bb_idx)
+ m_highest_bb_idx = bb_idx;
+
+ size_t new_size = m_highest_bb_idx + 1;
+ if (basic_block_info_for_fn (cfun)->length () < new_size)
+ vec_safe_grow_cleared (basic_block_info_for_fn (cfun), new_size);
+
+ last_basic_block_for_fn (cfun) = new_size;
+
+ /* Create the basic block.
+
+ We can't call create_basic_block and use the regular RTL block-creation
+ hooks, since this creates NOTE_INSN_BASIC_BLOCK instances. We don't
+ want to do that; we want to use the notes we were provided with. */
+ basic_block bb = alloc_block ();
+ init_rtl_bb_info (bb);
+ bb->index = bb_idx;
+ bb->flags = BB_NEW | BB_RTL;
+ link_block (bb, m_bb_to_insert_after);
+ m_bb_to_insert_after = bb;
+
+ n_basic_blocks_for_fn (cfun)++;
+ SET_BASIC_BLOCK_FOR_FN (cfun, bb_idx, bb);
+ BB_SET_PARTITION (bb, BB_UNPARTITIONED);
+
+ /* Handle insns, edge-from and edge-to directives. */
+ while (1)
+ {
+ int c = read_skip_spaces ();
+ file_location loc = get_current_location ();
+ if (c == ')')
+ break;
+ else if (c == '(')
+ {
+ struct md_name directive;
+ read_name (&directive);
+ if (strcmp (directive.string, "edge-from") == 0)
+ parse_edge (bb, true);
+ else if (strcmp (directive.string, "edge-to") == 0)
+ parse_edge (bb, false);
+ else
+ {
+ rtx_insn *insn = parse_insn (loc, directive.string);
+ set_block_for_insn (insn, bb);
+ if (!BB_HEAD (bb))
+ BB_HEAD (bb) = insn;
+ BB_END (bb) = insn;
+ }
+ }
+ else
+ fatal_at (loc, "expected '(' or ')'");
+ }
+}
+
+/* Subroutine of function_reader::parse_edge.
+ Parse a basic block index, handling "entry" and "exit". */
+
+int
+function_reader::parse_bb_idx ()
+{
+ struct md_name name;
+ read_name (&name);
+ if (strcmp (name.string, "entry") == 0)
+ return ENTRY_BLOCK;
+ if (strcmp (name.string, "exit") == 0)
+ return EXIT_BLOCK;
+ return atoi (name.string);
+}
+
+/* Subroutine of parse_edge_flags.
+ Parse a token such as "FALLTHRU", converting to the flag value.
+ Issue an error if the token is unrecognized. */
+
+static int
+parse_edge_flag_token (const char *tok)
+{
+#define DEF_EDGE_FLAG(NAME,IDX) \
+ do { \
+ if (strcmp (tok, #NAME) == 0) \
+ return EDGE_##NAME; \
+ } while (0);
+#include "cfg-flags.def"
+#undef DEF_EDGE_FLAG
+ error ("unrecognized edge flag: '%s'", tok);
+ return 0;
+}
+
+/* Subroutine of function_reader::parse_edge.
+ Parse STR and convert to a flag value (or issue an error).
+ The parser uses strtok and hence modifiers STR in-place. */
+
+static int
+parse_edge_flags (char *str)
+{
+ int result = 0;
+
+ char *tok = strtok (str, "| ");
+ while (tok)
+ {
+ result |= parse_edge_flag_token (tok);
+ tok = strtok (NULL, "| ");
+ }
+
+ return result;
+}
+
+/* Parse an "edge-from" or "edge-to" directive within the "block"
+ directive for BLOCK, having already parsed the "(edge" heading.
+ Consume the final ")". Record the edge within m_deferred_edges. */
+
+void
+function_reader::parse_edge (basic_block block, bool from)
+{
+ gcc_assert (block);
+ int this_bb_idx = block->index;
+ file_location loc = get_current_location ();
+ int other_bb_idx = parse_bb_idx ();
+
+ /* "(edge-from 2)" means src = 2, dest = this_bb_idx, whereas
+ "(edge-to 3)" means src = this_bb_idx, dest = 3. */
+ int src_idx = from ? other_bb_idx : this_bb_idx;
+ int dest_idx = from ? this_bb_idx : other_bb_idx;
+
+ /* Optional "(flags)". */
+ int flags = 0;
+ int c = read_skip_spaces ();
+ if (c == '(')
+ {
+ require_word_ws ("flags");
+ require_char_ws ('"');
+ char *str = read_quoted_string ();
+ flags = parse_edge_flags (str);
+ require_char_ws (')');
+ }
+ else
+ unread_char (c);
+
+ require_char_ws (')');
+
+ /* This BB already exists, but the other BB might not yet.
+ For now, save the edges, and create them at the end of insn-chain
+ processing. */
+ /* For now, only process the (edge-from) to this BB, and (edge-to)
+ that go to the exit block.
+ FIXME: we don't yet verify that the edge-from and edge-to directives
+ are consistent. */
+ if (from || dest_idx == EXIT_BLOCK)
+ m_deferred_edges.safe_push (deferred_edge (loc, src_idx, dest_idx, flags));
+}
+
+/* Parse rtx instructions by calling read_rtx_code, calling
+ set_first_insn and set_last_insn as appropriate, and
+ adding the insn to the insn chain.
+ Consume the trailing ')'. */
+
+rtx_insn *
+function_reader::parse_insn (file_location start_loc, const char *name)
+{
+ rtx x = read_rtx_code (name);
+ if (!x)
+ fatal_at (start_loc, "expected insn type; got '%s'", name);
+ rtx_insn *insn = dyn_cast <rtx_insn *> (x);
+ if (!insn)
+ fatal_at (start_loc, "expected insn type; got '%s'", name);
+
+ /* Consume the trailing ')'. */
+ require_char_ws (')');
+
+ rtx_insn *last_insn = get_last_insn ();
+
+ /* Add "insn" to the insn chain. */
+ if (last_insn)
+ {
+ gcc_assert (NEXT_INSN (last_insn) == NULL);
+ SET_NEXT_INSN (last_insn) = insn;
+ }
+ SET_PREV_INSN (insn) = last_insn;
+
+ /* Add it to the sequence. */
+ set_last_insn (insn);
+ if (!m_first_insn)
+ {
+ m_first_insn = insn;
+ set_first_insn (insn);
+ }
+
+ if (rtx_code_label *label = dyn_cast <rtx_code_label *> (insn))
+ maybe_set_max_label_num (label);
+
+ return insn;
+}
+
+/* Postprocessing subroutine for parse_insn_chain: all the basic blocks
+ should have been created by now; create the edges that were seen. */
+
+void
+function_reader::create_edges ()
+{
+ int i;
+ deferred_edge *de;
+ FOR_EACH_VEC_ELT (m_deferred_edges, i, de)
+ {
+ /* The BBs should already have been created by parse_block. */
+ basic_block src = BASIC_BLOCK_FOR_FN (cfun, de->m_src_bb_idx);
+ if (!src)
+ fatal_at (de->m_loc, "error: block index %i not found",
+ de->m_src_bb_idx);
+ basic_block dst = BASIC_BLOCK_FOR_FN (cfun, de->m_dest_bb_idx);
+ if (!dst)
+ fatal_at (de->m_loc, "error: block with index %i not found",
+ de->m_dest_bb_idx);
+ unchecked_make_edge (src, dst, de->m_flags);
+ }
+}
+
+/* Parse a "crtl" directive, having already parsed the "(crtl" heading.
+ Consume the final ")". */
+
+void
+function_reader::parse_crtl (file_location loc)
+{
+ if (m_have_crtl_directive)
+ error_at (loc, "more than one 'crtl' directive");
+ m_have_crtl_directive = true;
+
+ /* return_rtx. */
+ require_char_ws ('(');
+ require_word_ws ("return_rtx");
+ crtl->return_rtx = parse_rtx ();
+ require_char_ws (')');
+
+ require_char_ws (')');
+}
+
+/* Parse operand IDX of X, returning X, or an equivalent rtx
+ expression (for consolidating singletons).
+ This is an overridden implementation of rtx_reader::read_rtx_operand for
+ function_reader, handling various extra data printed by print_rtx,
+ and sometimes calling the base class implementation. */
+
+rtx
+function_reader::read_rtx_operand (rtx x, int idx)
+{
+ RTX_CODE code = GET_CODE (x);
+ const char *format_ptr = GET_RTX_FORMAT (code);
+ const char format_char = format_ptr[idx];
+ struct md_name name;
+
+ /* Override the regular parser for some format codes. */
+ switch (format_char)
+ {
+ case 'e':
+ if (idx == 7 && CALL_P (x))
+ {
+ m_in_call_function_usage = true;
+ return rtx_reader::read_rtx_operand (x, idx);
+ m_in_call_function_usage = false;
+ }
+ else
+ return rtx_reader::read_rtx_operand (x, idx);
+ break;
+
+ case 'u':
+ read_rtx_operand_u (x, idx);
+ /* Don't run regular parser for 'u'. */
+ return x;
+
+ case 'i':
+ case 'n':
+ read_rtx_operand_i_or_n (x, idx, format_char);
+ /* Don't run regular parser for these codes. */
+ return x;
+
+ case 'B':
+ gcc_assert (is_compact ());
+ /* Compact mode doesn't store BBs. */
+ /* Don't run regular parser. */
+ return x;
+
+ case 'r':
+ /* Don't run regular parser for 'r'. */
+ return read_rtx_operand_r (x);
+
+ default:
+ break;
+ }
+
+ /* Call base class implementation. */
+ x = rtx_reader::read_rtx_operand (x, idx);
+
+ /* Handle any additional parsing needed to handle what the dump
+ could contain. */
+ switch (format_char)
+ {
+ case '0':
+ extra_parsing_for_operand_code_0 (x, idx);
+ break;
+
+ case 'w':
+ if (!is_compact ())
+ {
+ /* Strip away the redundant hex dump of the value. */
+ require_char_ws ('[');
+ read_name (&name);
+ require_char_ws (']');
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return x;
+}
+
+/* Parse operand IDX of X, of code 'u', when reading function dumps.
+
+ The RTL file recorded the ID of an insn (or 0 for NULL); we
+ must store this as a pointer, but the insn might not have
+ been loaded yet. Store the ID away for now, via a fixup. */
+
+void
+function_reader::read_rtx_operand_u (rtx x, int idx)
+{
+ /* In compact mode, the PREV/NEXT insn uids are not dumped, so skip
+ the "uu" when reading. */
+ if (is_compact () && GET_CODE (x) != LABEL_REF)
+ return;
+
+ struct md_name name;
+ file_location loc = read_name (&name);
+ int insn_id = atoi (name.string);
+ if (insn_id)
+ add_fixup_insn_uid (loc, x, idx, insn_id);
+}
+
+/* Read a name, looking for a match against a string found in array
+ STRINGS of size NUM_VALUES.
+ Return the index of the the matched string, or emit an error. */
+
+int
+function_reader::parse_enum_value (int num_values, const char *const *strings)
+{
+ struct md_name name;
+ read_name (&name);
+ for (int i = 0; i < num_values; i++)
+ {
+ if (strcmp (name.string, strings[i]) == 0)
+ return i;
+ }
+ error ("unrecognized enum value: '%s'", name.string);
+ return 0;
+}
+
+/* Parse operand IDX of X, of code 'i' or 'n'.
+ Special-cased handling of these, for reading function dumps. */
+
+void
+function_reader::read_rtx_operand_i_or_n (rtx x, int idx,
+ char format_char)
+{
+ /* Handle some of the extra information that print_rtx
+ can write out for these cases. */
+ /* print_rtx only writes out operand 5 for notes
+ for NOTE_KIND values NOTE_INSN_DELETED_LABEL
+ and NOTE_INSN_DELETED_DEBUG_LABEL. */
+ if (idx == 5 && NOTE_P (x))
+ return;
+
+ if (idx == 4 && INSN_P (x))
+ {
+ maybe_read_location (idx, x);
+ return;
+ }
+
+ /* INSN_CODEs aren't printed in compact mode, so don't attempt to
+ parse them. */
+ if (is_compact ()
+ && INSN_P (x)
+ && &INSN_CODE (x) == &XINT (x, idx))
+ {
+ INSN_CODE (x) = -1;
+ return;
+ }
+
+ /* Handle UNSPEC and UNSPEC_VOLATILE's operand 1. */
+#if !defined(GENERATOR_FILE) && NUM_UNSPECV_VALUES > 0
+ if (idx == 1
+ && GET_CODE (x) == UNSPEC_VOLATILE)
+ {
+ XINT (x, 1)
+ = parse_enum_value (NUM_UNSPECV_VALUES, unspecv_strings);
+ return;
+ }
+#endif
+#if !defined(GENERATOR_FILE) && NUM_UNSPEC_VALUES > 0
+ if (idx == 1
+ && (GET_CODE (x) == UNSPEC
+ || GET_CODE (x) == UNSPEC_VOLATILE))
+ {
+ XINT (x, 1)
+ = parse_enum_value (NUM_UNSPEC_VALUES, unspec_strings);
+ return;
+ }
+#endif
+
+ struct md_name name;
+ read_name (&name);
+ int value;
+ if (format_char == 'n')
+ value = parse_note_insn_name (name.string);
+ else
+ value = atoi (name.string);
+ XINT (x, idx) = value;
+}
+
+/* Parse the 'r' operand of X, returning X, or an equivalent rtx
+ expression (for consolidating singletons).
+ Special-cased handling of code 'r' for reading function dumps. */
+
+rtx
+function_reader::read_rtx_operand_r (rtx x)
+{
+ struct md_name name;
+ file_location loc = read_name (&name);
+ int regno = lookup_reg_by_dump_name (name.string);
+ if (regno == -1)
+ fatal_at (loc, "unrecognized register: '%s'", name.string);
+
+ set_regno_raw (x, regno, 1);
+
+ /* Consolidate singletons. */
+ x = consolidate_singletons (x);
+
+ ORIGINAL_REGNO (x) = regno;
+
+ /* Parse extra stuff at end of 'r'.
+ We may have zero, one, or two sections marked by square
+ brackets. */
+ int ch = read_skip_spaces ();
+ bool expect_original_regno = false;
+ if (ch == '[')
+ {
+ file_location loc = get_current_location ();
+ char *desc = read_until ("]", true);
+ strip_trailing_whitespace (desc);
+ const char *desc_start = desc;
+ /* If ORIGINAL_REGNO (rtx) != regno, we will have:
+ "orig:%i", ORIGINAL_REGNO (rtx).
+ Consume it, we don't set ORIGINAL_REGNO, since we can
+ get that from the 2nd copy later. */
+ if (0 == strncmp (desc, "orig:", 5))
+ {
+ expect_original_regno = true;
+ desc_start += 5;
+ /* Skip to any whitespace following the integer. */
+ const char *space = strchr (desc_start, ' ');
+ if (space)
+ desc_start = space + 1;
+ }
+ /* Any remaining text may be the REG_EXPR. Alternatively we have
+ no REG_ATTRS, and instead we have ORIGINAL_REGNO. */
+ if (ISDIGIT (*desc_start))
+ {
+ /* Assume we have ORIGINAL_REGNO. */
+ ORIGINAL_REGNO (x) = atoi (desc_start);
+ }
+ else
+ {
+ /* Assume we have REG_EXPR. */
+ add_fixup_expr (loc, x, desc_start);
+ }
+ free (desc);
+ }
+ else
+ unread_char (ch);
+ if (expect_original_regno)
+ {
+ require_char_ws ('[');
+ char *desc = read_until ("]", true);
+ ORIGINAL_REGNO (x) = atoi (desc);
+ free (desc);
+ }
+
+ return x;
+}
+
+/* Additional parsing for format code '0' in dumps, handling a variety
+ of special-cases in print_rtx, when parsing operand IDX of X. */
+
+void
+function_reader::extra_parsing_for_operand_code_0 (rtx x, int idx)
+{
+ RTX_CODE code = GET_CODE (x);
+ int c;
+ struct md_name name;
+
+ if (idx == 1 && code == SYMBOL_REF)
+ {
+ /* Possibly wrote " [flags %#x]", SYMBOL_REF_FLAGS (in_rtx). */
+ c = read_skip_spaces ();
+ if (c == '[')
+ {
+ file_location loc = read_name (&name);
+ if (strcmp (name.string, "flags"))
+ error_at (loc, "was expecting `%s'", "flags");
+ read_name (&name);
+ SYMBOL_REF_FLAGS (x) = strtol (name.string, NULL, 16);
+
+ /* We can't reconstruct SYMBOL_REF_BLOCK; set it to NULL. */
+ if (SYMBOL_REF_HAS_BLOCK_INFO_P (x))
+ SYMBOL_REF_BLOCK (x) = NULL;
+
+ require_char (']');
+ }
+ else
+ unread_char (c);
+
+ /* If X had a non-NULL SYMBOL_REF_DECL,
+ rtx_writer::print_rtx_operand_code_0 would have dumped it
+ using print_node_brief.
+ Skip the content for now. */
+ c = read_skip_spaces ();
+ if (c == '<')
+ {
+ while (1)
+ {
+ char ch = read_char ();
+ if (ch == '>')
+ break;
+ }
+ }
+ else
+ unread_char (c);
+ }
+ else if (idx == 3 && code == NOTE)
+ {
+ /* Note-specific data appears for operand 3, which annoyingly
+ is before the enum specifying which kind of note we have
+ (operand 4). */
+ c = read_skip_spaces ();
+ if (c == '[')
+ {
+ /* Possibly data for a NOTE_INSN_BASIC_BLOCK, of the form:
+ [bb %d]. */
+ file_location bb_loc = read_name (&name);
+ if (strcmp (name.string, "bb"))
+ error_at (bb_loc, "was expecting `%s'", "bb");
+ read_name (&name);
+ int bb_idx = atoi (name.string);
+ add_fixup_note_insn_basic_block (bb_loc, x, idx,
+ bb_idx);
+ require_char_ws (']');
+ }
+ else
+ unread_char (c);
+ }
+}
+
+/* Implementation of rtx_reader::handle_any_trailing_information.
+ Handle the various additional information that print-rtl.c can
+ write after the regular fields, when parsing X. */
+
+void
+function_reader::handle_any_trailing_information (rtx x)
+{
+ struct md_name name;
+
+ switch (GET_CODE (x))
+ {
+ case MEM:
+ {
+ int ch;
+ require_char_ws ('[');
+ read_name (&name);
+ MEM_ALIAS_SET (x) = atoi (name.string);
+ /* We have either a MEM_EXPR, or a space. */
+ if (peek_char () != ' ')
+ {
+ file_location loc = get_current_location ();
+ char *desc = read_until (" +", false);
+ add_fixup_expr (loc, consolidate_singletons (x), desc);
+ free (desc);
+ }
+ else
+ read_char ();
+
+ /* We may optionally have '+' for MEM_OFFSET_KNOWN_P. */
+ ch = read_skip_spaces ();
+ if (ch == '+')
+ {
+ read_name (&name);
+ MEM_OFFSET_KNOWN_P (x) = 1;
+ MEM_OFFSET (x) = atoi (name.string);
+ }
+ else
+ unread_char (ch);
+
+ /* Handle optional " S" for MEM_SIZE. */
+ ch = read_skip_spaces ();
+ if (ch == 'S')
+ {
+ read_name (&name);
+ MEM_SIZE (x) = atoi (name.string);
+ }
+ else
+ unread_char (ch);
+
+ /* Handle optional " A" for MEM_ALIGN. */
+ ch = read_skip_spaces ();
+ if (ch == 'A' && peek_char () != 'S')
+ {
+ read_name (&name);
+ MEM_ALIGN (x) = 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 (x) = atoi (name.string);
+ }
+ else
+ unread_char (ch);
+
+ require_char (']');
+ }
+ break;
+
+ case CODE_LABEL:
+ /* Assume that LABEL_NUSES was not dumped. */
+ /* TODO: parse LABEL_KIND. */
+ /* For now, skip until closing ')'. */
+ do
+ {
+ char ch = read_char ();
+ if (ch == ')')
+ {
+ unread_char (ch);
+ break;
+ }
+ }
+ while (1);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* Parse a tree dump for a MEM_EXPR in DESC and turn it back into a tree.
+ We handle "<retval>", but for anything else we "cheat" by building a
+ global VAR_DECL of type "int" with that name (returning the same global
+ for a name if we see the same name more than once). */
+
+tree
+function_reader::parse_mem_expr (const char *desc)
+{
+ tree fndecl = cfun->decl;
+
+ if (0 == strcmp (desc, "<retval>"))
+ return DECL_RESULT (fndecl);
+
+ /* Search within function parms. */
+ for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
+ if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (arg))) == 0)
+ return arg;
+
+ /* Search within decls we already created.
+ FIXME: use a hash rather than linear search. */
+ int i;
+ tree t;
+ FOR_EACH_VEC_ELT (m_fake_scope, i, t)
+ if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (t))) == 0)
+ return t;
+
+ /* Not found? Create it.
+ This allows mimicking of real data but avoids having to specify
+ e.g. names of locals, params etc.
+ Though this way we don't know if we have a PARM_DECL vs a VAR_DECL,
+ and we don't know the types. Fake it by making everything be
+ a VAR_DECL of "int" type. */
+ t = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+ get_identifier (desc),
+ integer_type_node);
+ m_fake_scope.safe_push (t);
+ return t;
+}
+
+/* Record the information for later post-processing. */
+
+void
+function_reader::add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx,
+ int insn_uid)
+{
+ m_fixups.safe_push (new fixup_insn_uid (loc, insn, operand_idx, insn_uid));
+}
+
+/* Record the information for later post-processing. */
+
+void
+function_reader::add_fixup_note_insn_basic_block (file_location loc, rtx insn,
+ int operand_idx, int bb_idx)
+{
+ m_fixups.safe_push (new fixup_note_insn_basic_block (loc, insn, operand_idx,
+ bb_idx));
+}
+
+/* Record the information for later post-processing. */
+void
+function_reader::add_fixup_source_location (file_location, rtx,
+ int, const char *, int)
+{
+ /* Empty for now. */
+}
+
+/* Record the information for later post-processing. */
+
+void
+function_reader::add_fixup_expr (file_location loc, rtx insn,
+ const char *desc)
+{
+ gcc_assert (desc);
+ /* Fail early if the RTL reader erroneously hands us an int. */
+ gcc_assert (!ISDIGIT (desc[0]));
+
+ m_fixups.safe_push (new fixup_expr (loc, insn, desc));
+}
+
+/* Helper function for consolidate_reg. Return the global rtx for
+ the register with regno REGNO. */
+
+static rtx
+lookup_global_register (int regno)
+{
+ /* We can't use a switch here, as some of the REGNUMs might not be constants
+ for some targets. */
+ if (regno == STACK_POINTER_REGNUM)
+ return stack_pointer_rtx;
+ else if (regno == FRAME_POINTER_REGNUM)
+ return frame_pointer_rtx;
+ else if (regno == HARD_FRAME_POINTER_REGNUM)
+ return hard_frame_pointer_rtx;
+ else if (regno == ARG_POINTER_REGNUM)
+ return arg_pointer_rtx;
+ else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM)
+ return virtual_incoming_args_rtx;
+ else if (regno == VIRTUAL_STACK_VARS_REGNUM)
+ return virtual_stack_vars_rtx;
+ else if (regno == VIRTUAL_STACK_DYNAMIC_REGNUM)
+ return virtual_stack_dynamic_rtx;
+ else if (regno == VIRTUAL_OUTGOING_ARGS_REGNUM)
+ return virtual_outgoing_args_rtx;
+ else if (regno == VIRTUAL_CFA_REGNUM)
+ return virtual_cfa_rtx;
+ else if (regno == VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM)
+ return virtual_preferred_stack_boundary_rtx;
+#ifdef return_ADDRESS_POINTER_REGNUM
+ else if (regno == RETURN_ADDRESS_POINTER_REGNUM)
+ return return_address_pointer_rtx;
+#endif
+
+ return NULL;
+}
+
+/* Ensure that the backend can cope with a REG with regno REGNO.
+ Normally REG instances are created by gen_reg_rtx which updates
+ regno_reg_rtx, growing it as necessary.
+ The REG instances created from the dumpfile weren't created this
+ way, so we need to manually update regno_reg_rtx. */
+
+static void
+ensure_regno (int regno)
+{
+ if (reg_rtx_no < regno + 1)
+ reg_rtx_no = regno + 1;
+
+ crtl->emit.ensure_regno_capacity ();
+ gcc_assert (regno < crtl->emit.regno_pointer_align_length);
+}
+
+/* Helper function for consolidate_singletons, for handling REG instances.
+ Given REG instance X of some regno, return the singleton rtx for that
+ regno, if it exists, or X. */
+
+static rtx
+consolidate_reg (rtx x)
+{
+ gcc_assert (GET_CODE (x) == REG);
+
+ unsigned int regno = REGNO (x);
+
+ ensure_regno (regno);
+
+ /* Some register numbers have their rtx created in init_emit_regs
+ e.g. stack_pointer_rtx for STACK_POINTER_REGNUM.
+ Consolidate on this. */
+ rtx global_reg = lookup_global_register (regno);
+ if (global_reg)
+ return global_reg;
+
+ /* Populate regno_reg_rtx if necessary. */
+ if (regno_reg_rtx[regno] == NULL)
+ regno_reg_rtx[regno] = x;
+ /* Use it. */
+ gcc_assert (GET_CODE (regno_reg_rtx[regno]) == REG);
+ gcc_assert (REGNO (regno_reg_rtx[regno]) == regno);
+ if (GET_MODE (x) == GET_MODE (regno_reg_rtx[regno]))
+ return regno_reg_rtx[regno];
+
+ return x;
+}
+
+/* When reading RTL function dumps, we must consolidate some
+ rtx so that we use singletons where singletons are expected
+ (e.g. we don't want multiple "(const_int 0 [0])" rtx, since
+ these are tested via pointer equality against const0_rtx.
+
+ Return the equivalent singleton rtx for X, if any, otherwise X. */
+
+rtx
+function_reader::consolidate_singletons (rtx x)
+{
+ if (!x)
+ return x;
+
+ switch (GET_CODE (x))
+ {
+ case PC: return pc_rtx;
+ case RETURN: return ret_rtx;
+ case SIMPLE_RETURN: return simple_return_rtx;
+ case CC0: return cc0_rtx;
+
+ case REG:
+ return consolidate_reg (x);
+
+ case CONST_INT:
+ return gen_rtx_CONST_INT (GET_MODE (x), INTVAL (x));
+
+ default:
+ break;
+ }
+
+ return x;
+}
+
+/* Parse an rtx directive, including both the opening/closing parentheses,
+ and the name. */
+
+rtx
+function_reader::parse_rtx ()
+{
+ require_char_ws ('(');
+ struct md_name directive;
+ read_name (&directive);
+ rtx result
+ = consolidate_singletons (read_rtx_code (directive.string));
+ require_char_ws (')');
+
+ return result;
+}
+
+/* Implementation of rtx_reader::postprocess for reading function dumps. */
+
+rtx
+function_reader::postprocess (rtx x)
+{
+ return consolidate_singletons (x);
+}
+
+/* Implementation of rtx_reader::finalize_string for reading function dumps.
+ Make a GC-managed copy of STRINGBUF. */
+
+const char *
+function_reader::finalize_string (char *stringbuf)
+{
+ return ggc_strdup (stringbuf);
+}
+
+/* Handle the optional location information written by print_rtx for
+ instructions. Specifically, operand 4 of instructions (of type "i')
+ is printed thus:
+
+ if (INSN_HAS_LOCATION (in_insn))
+ {
+ expanded_location xloc = insn_location (in_insn);
+ fprintf (outfile, " %s:%i", xloc.file, xloc.line);
+ }
+
+ Hence we need to speculatively read a location of the form
+ " %s:%i", and unread the content if there wasn't one.
+
+ Assume that filenames can't contain whitespace, and can't
+ contain ':'. */
+
+void
+function_reader::maybe_read_location (int operand_idx, rtx insn)
+{
+ file_location loc = get_current_location ();
+
+ /* Skip to first non-whitespace. */
+ int ch = read_skip_spaces ();
+ auto_vec<char> buf;
+ buf.safe_push (ch);
+ while (1)
+ {
+ int ch = read_char ();
+ /* If we see a ':', assume we have a filename. */
+ if (ch == ':')
+ {
+ buf.safe_push ('\0');
+ break;
+ }
+ buf.safe_push (ch);
+
+ /* If we see a space before ':', assume we don't have a
+ filename. */
+ if (ISSPACE (ch))
+ {
+ while (!buf.is_empty ())
+ unread_char (buf.pop ());
+ return;
+ }
+ }
+ char *filename = buf.address ();
+ struct md_name name;
+ read_name (&name);
+
+ add_fixup_source_location (loc, insn, operand_idx,
+ filename, atoi (name.string));
+}
+
+/* Postprocessing subroutine of function_reader::parse_function.
+ Populate m_insns_by_uid. */
+
+void
+function_reader::handle_insn_uids ()
+{
+ /* Locate the currently assigned INSN_UID values, storing
+ them in m_insns_by_uid. */
+ int max_uid = 0;
+ for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ {
+ if (m_insns_by_uid.get (INSN_UID (insn)))
+ error ("duplicate insn UID: %i", INSN_UID (insn));
+ m_insns_by_uid.put (INSN_UID (insn), insn);
+ if (INSN_UID (insn) > max_uid)
+ max_uid = INSN_UID (insn);
+ }
+
+ /* Ensure x_cur_insn_uid is 1 more than the biggest insn UID seen.
+ This is normally updated by the various make_*insn_raw functions. */
+ crtl->emit.x_cur_insn_uid = max_uid + 1;
+}
+
+/* Apply all of the recorded fixups. */
+
+void
+function_reader::apply_fixups ()
+{
+ int i;
+ fixup *f;
+ FOR_EACH_VEC_ELT (m_fixups, i, f)
+ f->apply (this);
+}
+
+/* Given a UID value, try to locate a pointer to the corresponding
+ rtx_insn *, or NULL if if can't be found. */
+
+rtx_insn **
+function_reader::get_insn_by_uid (int uid)
+{
+ return m_insns_by_uid.get (uid);
+}
+
+/* Run the RTL dump parser. */
+
+bool
+read_rtl_function_body (int argc, const char **argv,
+ bool (*parse_opt) (const char *))
+{
+ initialize_rtl ();
+ init_emit ();
+ init_varasm_status ();
+
+ function_reader reader;
+ if (!reader.read_md_files (argc, argv, parse_opt))
+ return false;
+
+ return true;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that parse_edge_flags works. */
+
+static void
+test_edge_flags ()
+{
+ /* parse_edge_flags modifies its input (due to strtok), so we must make
+ a copy of the literals. */
+#define ASSERT_PARSE_EDGE_FLAGS(EXPECTED, STR) \
+ do { \
+ char *str = xstrdup (STR); \
+ ASSERT_EQ (EXPECTED, parse_edge_flags (str)); \
+ free (str); \
+ } while (0)
+
+ ASSERT_PARSE_EDGE_FLAGS (0, "");
+ ASSERT_PARSE_EDGE_FLAGS (EDGE_FALLTHRU, "FALLTHRU");
+ ASSERT_PARSE_EDGE_FLAGS (EDGE_ABNORMAL_CALL, "ABNORMAL_CALL");
+ ASSERT_PARSE_EDGE_FLAGS (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL,
+ "ABNORMAL | ABNORMAL_CALL");
+
+#undef ASSERT_PARSE_EDGE_FLAGS
+}
+
+/* Verify that lookup_reg_by_dump_name works. */
+
+static void
+test_parsing_regnos ()
+{
+ ASSERT_EQ (-1, lookup_reg_by_dump_name ("this is not a register"));
+
+ /* Verify lookup of hard registers. */
+#ifdef GCC_AARCH64_H
+ ASSERT_EQ (0, lookup_reg_by_dump_name ("x0"));
+ ASSERT_EQ (1, lookup_reg_by_dump_name ("x1"));
+#endif
+#ifdef I386_OPTS_H
+ ASSERT_EQ (0, lookup_reg_by_dump_name ("ax"));
+ ASSERT_EQ (1, lookup_reg_by_dump_name ("dx"));
+#endif
+
+ /* Verify lookup of virtual registers. */
+ ASSERT_EQ (VIRTUAL_INCOMING_ARGS_REGNUM,
+ lookup_reg_by_dump_name ("virtual-incoming-args"));
+ ASSERT_EQ (VIRTUAL_STACK_VARS_REGNUM,
+ lookup_reg_by_dump_name ("virtual-stack-vars"));
+ ASSERT_EQ (VIRTUAL_STACK_DYNAMIC_REGNUM,
+ lookup_reg_by_dump_name ("virtual-stack-dynamic"));
+ ASSERT_EQ (VIRTUAL_OUTGOING_ARGS_REGNUM,
+ lookup_reg_by_dump_name ("virtual-outgoing-args"));
+ ASSERT_EQ (VIRTUAL_CFA_REGNUM,
+ lookup_reg_by_dump_name ("virtual-cfa"));
+ ASSERT_EQ (VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM,
+ lookup_reg_by_dump_name ("virtual-preferred-stack-boundary"));
+
+ /* Verify lookup of non-virtual pseudos. */
+ ASSERT_EQ (LAST_VIRTUAL_REGISTER + 1, lookup_reg_by_dump_name ("%0"));
+ ASSERT_EQ (LAST_VIRTUAL_REGISTER + 2, lookup_reg_by_dump_name ("%1"));
+}
+
+/* Run all of the selftests within this file. */
+
+void
+read_rtl_function_c_tests ()
+{
+ test_edge_flags ();
+ test_parsing_regnos ();
+}
+
+} // 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..b2a6e81
--- /dev/null
+++ b/gcc/read-rtl-function.h
@@ -0,0 +1,26 @@
+/* read-rtl-function.h - Reader for RTL function dumps
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_READ_RTL_FUNCTION_H
+#define GCC_READ_RTL_FUNCTION_H
+
+extern bool read_rtl_function_body (int argc, const char **argv,
+ bool (*parse_opt) (const char *));
+
+#endif /* GCC_READ_RTL_FUNCTION_H */
diff --git a/gcc/read-rtl.c b/gcc/read-rtl.c
index f74c875..eb3b59e 100644
--- a/gcc/read-rtl.c
+++ b/gcc/read-rtl.c
@@ -17,7 +17,13 @@ You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
+/* This file is compiled twice: once for the generator programs
+ once for the compiler. */
+#ifdef GENERATOR_FILE
#include "bconfig.h"
+#else
+#include "config.h"
+#endif
/* Disable rtl checking; it conflicts with the iterator handling. */
#undef ENABLE_RTL_CHECKING
@@ -30,6 +36,12 @@ along with GCC; see the file COPYING3. If not see
#include "read-md.h"
#include "gensupport.h"
+#ifndef GENERATOR_FILE
+#include "function.h"
+#include "memmodel.h"
+#include "emit-rtl.h"
+#endif
+
/* One element in a singly-linked list of (integer, string) pairs. */
struct map_value {
struct map_value *next;
@@ -106,6 +118,7 @@ htab_t subst_attr_to_iter_map = NULL;
const char *current_iterator_name;
static void validate_const_int (const char *);
+static void one_time_initialization (void);
/* Global singleton. */
rtx_reader *rtx_reader_ptr = NULL;
@@ -142,6 +155,25 @@ apply_mode_iterator (void *loc, int mode)
PUT_MODE ((rtx) loc, (machine_mode) mode);
}
+/* In compact dumps, the code of insns is prefixed with "c", giving "cinsn",
+ "cnote" etc, and CODE_LABEL is special-cased as "clabel". */
+
+struct compact_insn_name {
+ RTX_CODE code;
+ const char *name;
+};
+
+static const compact_insn_name compact_insn_names[] = {
+ { DEBUG_INSN, "cdebug_insn" },
+ { INSN, "cinsn" },
+ { JUMP_INSN, "cjump_insn" },
+ { CALL_INSN, "ccall_insn" },
+ { JUMP_TABLE_DATA, "cjump_table_data" },
+ { BARRIER, "cbarrier" },
+ { CODE_LABEL, "clabel" },
+ { NOTE, "cnote" }
+};
+
/* Implementations of the iterator_group callbacks for codes. */
static int
@@ -153,6 +185,10 @@ find_code (const char *name)
if (strcmp (GET_RTX_NAME (i), name) == 0)
return i;
+ for (i = 0; i < (signed)ARRAY_SIZE (compact_insn_names); i++)
+ if (strcmp (compact_insn_names[i].name, name) == 0)
+ return compact_insn_names[i].code;
+
fatal_with_file_and_line ("unknown rtx code `%s'", name);
}
@@ -181,6 +217,8 @@ apply_int_iterator (void *loc, int value)
*(int *)loc = value;
}
+#ifdef GENERATOR_FILE
+
/* This routine adds attribute or does nothing depending on VALUE. When
VALUE is 1, it does nothing - the first duplicate of original
template is kept untouched when it's subjected to a define_subst.
@@ -252,6 +290,8 @@ bind_subst_iter_and_attr (const char *iter, const char *attr)
*slot = value;
}
+#endif /* #ifdef GENERATOR_FILE */
+
/* Return name of a subst-iterator, corresponding to subst-attribute ATTR. */
static char*
@@ -418,6 +458,8 @@ md_reader::copy_rtx_for_iterators (rtx original)
return x;
}
+#ifdef GENERATOR_FILE
+
/* Return a condition that must satisfy both ORIGINAL and EXTRA. If ORIGINAL
has the form "&& ..." (as used in define_insn_and_splits), assume that
EXTRA is already satisfied. Empty strings are treated like "true". */
@@ -581,6 +623,7 @@ apply_iterators (rtx original, vec<rtx> *queue)
}
}
}
+#endif /* #ifdef GENERATOR_FILE */
/* Add a new "mapping" structure to hashtable TABLE. NAME is the name
of the mapping and GROUP is the group to which it belongs. */
@@ -655,7 +698,9 @@ initialize_iterators (void)
substs.iterators = htab_create (13, leading_string_hash,
leading_string_eq_p, 0);
substs.find_builtin = find_int; /* We don't use it, anyway. */
+#ifdef GENERATOR_FILE
substs.apply_iterator = apply_subst_iterator;
+#endif
lower = add_mapping (&modes, modes.attrs, "mode");
upper = add_mapping (&modes, modes.attrs, "MODE");
@@ -724,6 +769,8 @@ atoll (const char *p)
}
#endif
+
+#ifdef GENERATOR_FILE
/* Process a define_conditions directive, starting with the optional
space after the "define_conditions". The directive looks like this:
@@ -765,6 +812,7 @@ md_reader::read_conditions ()
add_c_test (expr, value);
}
}
+#endif /* #ifdef GENERATOR_FILE */
static void
validate_const_int (const char *string)
@@ -861,6 +909,8 @@ md_reader::record_potential_iterator_use (struct iterator_group *group,
}
}
+#ifdef GENERATOR_FILE
+
/* Finish reading a declaration of the form:
(define... <name> [<value1> ... <valuen>])
@@ -1020,14 +1070,7 @@ check_code_iterator (struct mapping *iterator)
bool
rtx_reader::read_rtx (const char *rtx_name, vec<rtx> *rtxen)
{
- static bool initialized = false;
-
- /* Do one-time initialization. */
- if (!initialized)
- {
- initialize_iterators ();
- initialized = true;
- }
+ one_time_initialization ();
/* Handle various rtx-related declarations that aren't themselves
encoded as rtxes. */
@@ -1082,6 +1125,103 @@ rtx_reader::read_rtx (const char *rtx_name, vec<rtx> *rtxen)
return true;
}
+#endif /* #ifdef GENERATOR_FILE */
+
+/* Do one-time initialization. */
+
+static void
+one_time_initialization (void)
+{
+ static bool initialized = false;
+
+ if (!initialized)
+ {
+ initialize_iterators ();
+ initialized = true;
+ }
+}
+
+/* Consume characters until encountering a character in TERMINATOR_CHARS,
+ consuming the terminator character if CONSUME_TERMINATOR is true.
+ Return all characters before the terminator as an allocated buffer. */
+
+char *
+rtx_reader::read_until (const char *terminator_chars, bool consume_terminator)
+{
+ int ch = read_skip_spaces ();
+ unread_char (ch);
+ auto_vec<char> buf;
+ while (1)
+ {
+ ch = read_char ();
+ if (strchr (terminator_chars, ch))
+ {
+ if (!consume_terminator)
+ unread_char (ch);
+ break;
+ }
+ buf.safe_push (ch);
+ }
+ buf.safe_push ('\0');
+ return xstrdup (buf.address ());
+}
+
+/* Subroutine of read_rtx_code, for parsing zero or more flags. */
+
+static void
+read_flags (rtx return_rtx)
+{
+ while (1)
+ {
+ int ch = read_char ();
+ if (ch != '/')
+ {
+ unread_char (ch);
+ break;
+ }
+
+ int flag_char = read_char ();
+ switch (flag_char)
+ {
+ case 's':
+ RTX_FLAG (return_rtx, in_struct) = 1;
+ break;
+ case 'v':
+ RTX_FLAG (return_rtx, volatil) = 1;
+ break;
+ case 'u':
+ RTX_FLAG (return_rtx, unchanging) = 1;
+ break;
+ case 'f':
+ RTX_FLAG (return_rtx, frame_related) = 1;
+ break;
+ case 'j':
+ RTX_FLAG (return_rtx, jump) = 1;
+ break;
+ case 'c':
+ RTX_FLAG (return_rtx, call) = 1;
+ break;
+ case 'i':
+ RTX_FLAG (return_rtx, return_val) = 1;
+ break;
+ default:
+ fatal_with_file_and_line ("unrecognized flag: `%c'", flag_char);
+ }
+ }
+}
+
+/* Return the numeric value n for GET_REG_NOTE_NAME (n) for STRING,
+ or fail if STRING isn't recognized. */
+
+static int
+parse_reg_note_name (const char *string)
+{
+ for (int i = 0; i < REG_NOTE_MAX; i++)
+ if (0 == strcmp (string, GET_REG_NOTE_NAME (i)))
+ return i;
+ fatal_with_file_and_line ("unrecognized REG_NOTE name: `%s'", string);
+}
+
/* Subroutine of read_rtx and read_nested_rtx. CODE_NAME is the name of
either an rtx code or a code iterator. Parse the rest of the rtx and
return it. */
@@ -1090,11 +1230,12 @@ rtx
rtx_reader::read_rtx_code (const char *code_name)
{
RTX_CODE code;
- struct mapping *iterator;
+ struct mapping *iterator = NULL;
const char *format_ptr;
struct md_name name;
rtx return_rtx;
int c;
+ long reuse_id = -1;
/* Linked list structure for making RTXs: */
struct rtx_list
@@ -1103,13 +1244,39 @@ rtx_reader::read_rtx_code (const char *code_name)
rtx value; /* Value of this node. */
};
+ one_time_initialization ();
+
+ /* Handle reuse_rtx ids e.g. "(0|scratch:DI)". */
+ if (ISDIGIT (code_name[0]))
+ {
+ reuse_id = atoi (code_name);
+ while (char ch = *code_name++)
+ if (ch == '|')
+ break;
+ }
+
+ /* Handle "reuse_rtx". */
+ if (strcmp (code_name, "reuse_rtx") == 0)
+ {
+ read_name (&name);
+ long idx = atoi (name.string);
+ /* Look it up by ID. */
+ gcc_assert (idx < m_reuse_rtx_by_id.length ());
+ return_rtx = m_reuse_rtx_by_id[idx];
+ return return_rtx;
+ }
+
/* If this code is an iterator, build the rtx using the iterator's
first value. */
+#ifdef GENERATOR_FILE
iterator = (struct mapping *) htab_find (codes.iterators, &code_name);
if (iterator != 0)
code = (enum rtx_code) iterator->values->number;
else
code = (enum rtx_code) codes.find_builtin (code_name);
+#else
+ code = (enum rtx_code) codes.find_builtin (code_name);
+#endif
/* If we end up with an insn expression then we free this space below. */
return_rtx = rtx_alloc (code);
@@ -1117,9 +1284,36 @@ rtx_reader::read_rtx_code (const char *code_name)
memset (return_rtx, 0, RTX_CODE_SIZE (code));
PUT_CODE (return_rtx, code);
+ if (reuse_id != -1)
+ {
+ /* Store away for later reuse. */
+ m_reuse_rtx_by_id.safe_grow_cleared (reuse_id + 1);
+ m_reuse_rtx_by_id[reuse_id] = return_rtx;
+ }
+
if (iterator)
record_iterator_use (iterator, return_rtx);
+ /* Check for flags. */
+ read_flags (return_rtx);
+
+ /* Read REG_NOTE names for EXPR_LIST and INSN_LIST. */
+ if ((GET_CODE (return_rtx) == EXPR_LIST
+ || GET_CODE (return_rtx) == INSN_LIST
+ || GET_CODE (return_rtx) == INT_LIST)
+ && !m_in_call_function_usage)
+ {
+ char ch = read_char ();
+ if (ch == ':')
+ {
+ read_name (&name);
+ PUT_MODE_RAW (return_rtx,
+ (machine_mode)parse_reg_note_name (name.string));
+ }
+ else
+ unread_char (ch);
+ }
+
/* If what follows is `: mode ', read it and
store the mode in the rtx. */
@@ -1132,8 +1326,24 @@ rtx_reader::read_rtx_code (const char *code_name)
else
unread_char (c);
+ if (INSN_CHAIN_CODE_P (code))
+ {
+ read_name (&name);
+ INSN_UID (return_rtx) = atoi (name.string);
+ }
+
+ /* Use the format_ptr to parse the various operands of this rtx.
+ read_rtx_operand is a vfunc, allowing the parser to vary between
+ parsing .md files and parsing .rtl dumps; the function_reader subclass
+ overrides the defaults when loading RTL dumps, to handle the
+ various extra attributes emitted by print_rtx. */
for (int idx = 0; format_ptr[idx] != 0; idx++)
- read_rtx_operand (return_rtx, idx);
+ return_rtx = read_rtx_operand (return_rtx, idx);
+
+ /* Call a vfunc to handle the various additional information that
+ print-rtl.c can write after the regular fields; does nothing when
+ parsing .md files. */
+ handle_any_trailing_information (return_rtx);
if (CONST_WIDE_INT_P (return_rtx))
{
@@ -1197,9 +1407,11 @@ rtx_reader::read_rtx_code (const char *code_name)
/* Subroutine of read_rtx_code. Parse operand IDX within RETURN_RTX,
based on the corresponding format character within GET_RTX_FORMAT
- for the GET_CODE (RETURN_RTX). */
+ for the GET_CODE (RETURN_RTX), and return RETURN_RTX.
+ This is a virtual function, so that function_reader can override
+ some parsing, and potentially return a different rtx. */
-void
+rtx
rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
{
RTX_CODE code = GET_CODE (return_rtx);
@@ -1217,6 +1429,9 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
break;
case 'e':
+ XEXP (return_rtx, idx) = read_nested_rtx ();
+ break;
+
case 'u':
XEXP (return_rtx, idx) = read_nested_rtx ();
break;
@@ -1273,7 +1488,6 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
{
char *stringbuf;
int star_if_braced;
- struct obstack *string_obstack = get_string_obstack ();
c = read_skip_spaces ();
unread_char (c);
@@ -1293,7 +1507,10 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
star_if_braced = (format_ptr[idx] == 'T');
stringbuf = read_string (star_if_braced);
+ if (!stringbuf)
+ break;
+#ifdef GENERATOR_FILE
/* For insn patterns, we want to provide a default name
based on the file and line, like "*foo.md:12", if the
given name is blank. These are only for define_insn and
@@ -1303,6 +1520,7 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
&& (GET_CODE (return_rtx) == DEFINE_INSN
|| GET_CODE (return_rtx) == DEFINE_INSN_AND_SPLIT))
{
+ struct obstack *string_obstack = get_string_obstack ();
char line_name[20];
const char *read_md_filename = get_filename ();
const char *fn = (read_md_filename ? read_md_filename : "rtx");
@@ -1348,11 +1566,20 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
if (m != 0)
record_iterator_use (m, return_rtx);
}
+#endif /* #ifdef GENERATOR_FILE */
+
+ /* "stringbuf" was allocated within string_obstack and thus has
+ the its lifetime restricted to that of the rtx_reader. This is
+ OK for the generator programs, but for non-generator programs,
+ XSTR and XTMPL fields are meant to be allocated in the GC-managed
+ heap. Hence we need to allocate a copy in the GC-managed heap
+ for the non-generator case. */
+ const char *string_ptr = finalize_string (stringbuf);
if (star_if_braced)
- XTMPL (return_rtx, idx) = stringbuf;
+ XTMPL (return_rtx, idx) = string_ptr;
else
- XSTR (return_rtx, idx) = stringbuf;
+ XSTR (return_rtx, idx) = string_ptr;
}
break;
@@ -1398,6 +1625,8 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
default:
gcc_unreachable ();
}
+
+ return return_rtx;
}
/* Read a nested rtx construct from the MD file and return it. */
@@ -1408,6 +1637,11 @@ rtx_reader::read_nested_rtx ()
struct md_name name;
rtx return_rtx;
+ /* In compact dumps, trailing "(nil)" values can be omitted.
+ Handle such dumps. */
+ if (peek_char () == ')')
+ return NULL_RTX;
+
require_char_ws ('(');
read_name (&name);
@@ -1418,6 +1652,8 @@ rtx_reader::read_nested_rtx ()
require_char_ws (')');
+ return_rtx = postprocess (return_rtx);
+
return return_rtx;
}
@@ -1454,8 +1690,9 @@ rtx_reader::read_rtx_variadic (rtx form)
/* Constructor for class rtx_reader. */
-rtx_reader::rtx_reader ()
-: md_reader ()
+rtx_reader::rtx_reader (bool compact)
+: md_reader (compact),
+ m_in_call_function_usage (false)
{
/* Set the global singleton pointer. */
rtx_reader_ptr = this;
diff --git a/gcc/rtl-tests.c b/gcc/rtl-tests.c
index 8edddfb..bd918a7 100644
--- a/gcc/rtl-tests.c
+++ b/gcc/rtl-tests.c
@@ -200,6 +200,7 @@ test_single_set ()
static void
test_uncond_jump ()
{
+ set_new_first_and_last_insn (NULL, NULL);
rtx_insn *label = gen_label_rtx ();
rtx jump_pat = gen_rtx_SET (pc_rtx,
gen_rtx_LABEL_REF (VOIDmode,
diff --git a/gcc/rtl.h b/gcc/rtl.h
index 7a44e3b..a9a63dc 100644
--- a/gcc/rtl.h
+++ b/gcc/rtl.h
@@ -3665,7 +3665,9 @@ extern void init_varasm_once (void);
extern rtx make_debug_expr_from_rtl (const_rtx);
/* In read-rtl.c */
+#ifdef GENERATOR_FILE
extern bool read_rtx (const char *, vec<rtx> *);
+#endif
/* In alias.c */
extern rtx canon_rtx (rtx);
diff --git a/gcc/selftest-rtl.c b/gcc/selftest-rtl.c
new file mode 100644
index 0000000..a1e56b1
--- /dev/null
+++ b/gcc/selftest-rtl.c
@@ -0,0 +1,82 @@
+/* Selftest support for RTL.
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "selftest.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "read-rtl-function.h"
+#include "read-md.h"
+#include "tree-core.h"
+#include "memmodel.h"
+#include "emit-rtl.h"
+#include "selftest-rtl.h"
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Constructor for selftest::rtl_dump_test.
+ Read a dumped RTL function from PATH.
+ Takes ownership of PATH, freeing in dtor.
+ Use LOC as the effective location when reporting failures. */
+
+rtl_dump_test::rtl_dump_test (const location &loc, char *path)
+ : m_path (path)
+{
+ /* Parse the tempfile. */
+ auto_vec<const char *> argv (2);
+ argv.safe_push (progname);
+ argv.safe_push (path);
+ bool read_ok
+ = read_rtl_function_body (argv.length (), argv.address (), NULL);
+ ASSERT_TRUE_AT (loc, read_ok);
+}
+
+/* Destructor for selftest::rtl_dump_test.
+ Cleanup global state relating to the function, and free the path. */
+
+selftest::rtl_dump_test::~rtl_dump_test ()
+{
+ /* Cleanups. */
+ current_function_decl = NULL;
+ free_after_compilation (cfun);
+ set_cfun (NULL);
+ free (m_path);
+}
+
+/* Get the insn with the given uid, or NULL if not found. */
+
+rtx_insn *
+get_insn_by_uid (int uid)
+{
+ for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (INSN_UID (insn) == uid)
+ return insn;
+
+ /* Not found. */
+ return NULL;
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/selftest-rtl.h b/gcc/selftest-rtl.h
index f505018..accb486 100644
--- a/gcc/selftest-rtl.h
+++ b/gcc/selftest-rtl.h
@@ -47,6 +47,23 @@ assert_rtl_dump_eq (const location &loc, const char *expected_dump, rtx x,
assert_rtl_dump_eq (SELFTEST_LOCATION, (EXPECTED_DUMP), (RTX), \
(REUSE_MANAGER))
+/* A class for testing RTL function dumps. */
+
+class rtl_dump_test
+{
+ public:
+ /* Takes ownership of PATH. */
+ rtl_dump_test (const location &loc, char *path);
+ ~rtl_dump_test ();
+
+ private:
+ char *m_path;
+};
+
+/* Get the insn with the given uid, or NULL if not found. */
+
+extern rtx_insn *get_insn_by_uid (int uid);
+
} /* end of namespace selftest. */
#endif /* #if CHECKING_P */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index c1cd97e..bf2b84a 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -72,6 +72,7 @@ selftest::run_tests ()
tree_c_tests ();
gimple_c_tests ();
rtl_tests_c_tests ();
+ read_rtl_function_c_tests ();
/* Higher-level tests, or for components that other selftests don't
rely on. */
diff --git a/gcc/selftest.h b/gcc/selftest.h
index c390873..d5afa6d 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -184,6 +184,7 @@ extern void hash_map_tests_c_tests ();
extern void hash_set_tests_c_tests ();
extern void input_c_tests ();
extern void pretty_print_c_tests ();
+extern void read_rtl_function_c_tests ();
extern void rtl_tests_c_tests ();
extern void selftest_c_tests ();
extern void spellcheck_c_tests ();
diff --git a/gcc/tree-dfa.c b/gcc/tree-dfa.c
index 0396feb..7ec62db 100644
--- a/gcc/tree-dfa.c
+++ b/gcc/tree-dfa.c
@@ -305,6 +305,11 @@ ssa_default_def (struct function *fn, tree var)
gcc_assert (VAR_P (var)
|| TREE_CODE (var) == PARM_DECL
|| TREE_CODE (var) == RESULT_DECL);
+
+ /* Always NULL_TREE for rtl function dumps. */
+ if (!fn->gimple_df)
+ return NULL_TREE;
+
in.var = (tree)&ind;
ind.uid = DECL_UID (var);
return DEFAULT_DEFS (fn)->find_with_hash ((tree)&in, DECL_UID (var));
--
1.8.5.3