@@ -1270,6 +1270,7 @@ OBJS = \
dwarf2cfi.o \
dwarf2out.o \
emit-rtl.o \
+ errors.o \
et-forest.o \
except.o \
explow.o \
@@ -1408,6 +1409,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 \
@@ -1435,6 +1439,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 \
@@ -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 */
@@ -28,8 +28,22 @@ 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, int);
+
+ const char *filename;
+ int lineno;
+ int colno;
+};
+
+inline file_location::file_location (const char *filename_in, int lineno_in, int colno_in)
+: filename (filename_in), lineno (lineno_in), colno (colno_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 *);
@@ -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);
@@ -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))
@@ -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;
@@ -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. */
@@ -347,7 +349,7 @@ read_skip_spaces (void)
if (c != '*')
{
unread_char (c);
- fatal_with_file_and_line ("stray '/' in file");
+ return '/';
}
prevc = 0;
@@ -446,8 +448,8 @@ peek_char (void)
/* 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;
@@ -485,8 +487,12 @@ read_name (struct md_name *name)
c = read_char ();
}
+ unread_char (c);
+ *out_loc = base_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;
@@ -507,6 +513,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 = base_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.
@@ -652,6 +688,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);
@@ -21,19 +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, int);
-
- const char *filename;
- int lineno;
- int colno;
-};
-
-inline file_location::file_location (const char *filename_in, int lineno_in, int colno_in)
-: filename (filename_in), lineno (lineno_in), colno (colno_in) {}
+#include "errors.h"
/* Holds one symbol or number in the .md file. */
struct md_name {
@@ -200,9 +188,17 @@ class rtx_reader : public base_rtx_reader
~rtx_reader ();
rtx read_rtx_code (const char *);
- rtx read_rtx_operand (rtx return_rtx, int idx);
+ virtual rtx read_rtx_operand (rtx return_rtx, int idx);
rtx read_nested_rtx (void);
rtx read_rtx_variadic (rtx);
+ char *read_until (const char *terminator_chars, bool consume_terminator);
+
+ virtual void handle_any_trailing_information (rtx) {}
+ virtual rtx postprocess (rtx x) { return x; }
+
+ protected:
+ /* Analogous to print-rtl.c's in_call_function_usage. */
+ bool m_in_call_function_usage;
};
/* Global singleton; constrast with base_rtx_reader_ptr above. */
@@ -247,7 +243,8 @@ extern int read_skip_spaces (void);
extern void require_char (char expected);
extern void require_char_ws (char expected);
extern void require_word_ws (const char *expected);
-extern void read_name (struct md_name *);
+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 *);
new file mode 100644
@@ -0,0 +1,2402 @@
+/* read-rtl-function.c - Reader for RTL function dumps
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "target.h"
+#include "tree.h"
+#include "gimple-expr.h"
+#include "diagnostic.h"
+#include "opts.h"
+#include "fold-const.h"
+#include "gimplify.h"
+#include "stor-layout.h"
+#include "debug.h"
+#include "convert.h"
+#include "langhooks.h"
+#include "langhooks-def.h"
+#include "common/common-target.h"
+#include "read-md.h"
+#include <mpfr.h>
+#include "rtl.h"
+#include "cfghooks.h"
+#include "stringpool.h"
+#include "function.h"
+#include "tree-cfg.h"
+#include "cfg.h"
+#include "basic-block.h"
+#include "cfgrtl.h"
+#include "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"
+
+/* Forward decls. */
+class function_reader;
+class fixup;
+
+/* Subclass of rtx_reader for reading function dumps. */
+
+class function_reader : public rtx_reader
+{
+ public:
+ function_reader (function_reader_policy *policy);
+ ~function_reader ();
+
+ /* Overridden vfuncs of class base_rtx_reader. */
+ void handle_unknown_directive (file_location, const char *) FINAL OVERRIDE;
+
+ /* Overridden vfuncs of class rtx_reader. */
+ rtx read_rtx_operand (rtx return_rtx, int idx) FINAL OVERRIDE;
+ void handle_any_trailing_information (rtx return_rtx) FINAL OVERRIDE;
+ rtx postprocess (rtx) FINAL OVERRIDE;
+
+ int get_pseudo_offset () const { return m_pseudo_offset; }
+ rtx_insn **get_insn_by_uid (int uid);
+ tree parse_mem_expr (const char *desc);
+
+ private:
+ void parse_function ();
+ void create_function ();
+ void parse_insn_chain ();
+ void parse_insn (file_location loc, const char *name);
+ void create_cfg_and_basic_blocks ();
+ void parse_cfg (file_location loc);
+ void parse_bb ();
+ void parse_edge ();
+ void parse_crtl (file_location loc);
+
+ void read_rtx_operand_u (rtx return_rtx, int idx);
+ void read_rtx_operand_i_or_n (rtx return_rtx, int idx, char format_char);
+ rtx extra_parsing_for_operand_code_0 (rtx return_rtx, int idx);
+ rtx extra_parsing_for_operand_code_r (rtx return_rtx);
+
+ 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 add_fixup_pseudo (file_location loc, rtx x);
+
+ rtx consolidate_singletons (rtx x);
+ void maybe_read_location (int operand_idx, rtx insn);
+
+ void apply_fixups ();
+ void recreate_implicit_cfg_edges ();
+
+ private:
+ function_reader_policy *m_policy;
+ struct uid_hash : int_hash <int, -1, -2> {};
+ hash_map<uid_hash, rtx_insn *> m_insns_by_uid;
+ auto_vec<fixup *> m_fixups;
+ bitmap_obstack m_bitmap_obstack;
+ bitmap_head m_dumped_pseudos;
+ bitmap_head m_bb_indices_in_insns;
+ bitmap_head m_bb_indices_in_cfg;
+ rtx_insn *m_first_insn;
+ auto_vec<tree> m_fake_scope;
+ char *m_name;
+ bool m_have_cfg_directive;
+ bool m_have_crtl_directive;
+ int m_pseudo_offset;
+};
+
+/* 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;
+};
+
+/* A concrete subclass of fixup (not operand_fixup): fix up
+ the regno of a pseudo REG to ensure that it is a pseudo
+ on the current target. */
+
+class fixup_pseudo : public fixup
+{
+ public:
+ fixup_pseudo (file_location loc, rtx x)
+ : fixup (loc, x)
+ {}
+
+ void apply (function_reader *reader) const;
+};
+
+/* 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 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 <rtx_insn *> (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));
+ }
+}
+
+/* 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 ();
+ }
+#if 0
+ if (!DECL_RTL_SET_P (expr))
+ SET_DECL_RTL (expr, m_rtx);
+#endif
+}
+
+/* Fix up the regno of a REG that ought to be a pseudo, based
+ on an offset generated from all of the pseudo regnos seen. */
+
+void
+fixup_pseudo::apply (function_reader *reader) const
+{
+ gcc_assert (GET_CODE (m_rtx) == REG);
+
+ int dumped_regno = REGNO (m_rtx);
+ int effective_regno = dumped_regno + reader->get_pseudo_offset ();
+ set_regno_raw (m_rtx, effective_regno, 1);
+
+ ORIGINAL_REGNO (m_rtx) = effective_regno;
+ // FIXME: do we want this? it makes comparing dumps easier
+}
+
+/* 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". */
+
+ /* Not found. */
+ return -1;
+}
+
+/* class function_reader : public rtx_reader */
+
+/* function_reader's constructor. */
+
+function_reader::function_reader (function_reader_policy *policy)
+: rtx_reader (),
+ m_policy (policy),
+ m_first_insn (NULL),
+ m_name (NULL),
+ m_have_cfg_directive (false),
+ m_have_crtl_directive (false),
+ m_pseudo_offset (0)
+{
+ bitmap_obstack_initialize (&m_bitmap_obstack);
+ bitmap_initialize (&m_dumped_pseudos, &m_bitmap_obstack);
+ bitmap_initialize (&m_bb_indices_in_insns, &m_bitmap_obstack);
+ bitmap_initialize (&m_bb_indices_in_cfg, &m_bitmap_obstack);
+ bitmap_set_bit (&m_bb_indices_in_insns, ENTRY_BLOCK);
+ bitmap_set_bit (&m_bb_indices_in_insns, EXIT_BLOCK);
+}
+
+/* function_reader's destructor. */
+
+function_reader::~function_reader ()
+{
+ int i;
+ fixup *f;
+ FOR_EACH_VEC_ELT (m_fixups, i, f)
+ delete f;
+
+ bitmap_obstack_release (&m_bitmap_obstack);
+
+ free (m_name);
+}
+
+/* Implementation of rtx_reader::handle_unknown_directive.
+
+ Require a top-level "function" elements, as emitted by
+ print_rtx_function, and parse it. */
+
+void
+function_reader::handle_unknown_directive (file_location start_loc,
+ const char *name)
+{
+ if (strcmp (name, "function"))
+ fatal_at (start_loc, "expected 'function'");
+
+ parse_function ();
+}
+
+/* Parse the output of print_rtx_function (or hand-written data in the
+ same format), having parsed the "(function" heading, and finishing
+ immediately before the final ")".
+
+ The "cfg" and "crtl" clauses are optional. */
+
+void
+function_reader::parse_function ()
+{
+ m_name = xstrdup (read_string (0));
+
+ create_function ();
+
+ require_char_ws ('(');
+ require_word_ws ("insn-chain");
+ parse_insn_chain ();
+
+ create_cfg_and_basic_blocks ();
+
+ 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, "cfg") == 0)
+ parse_cfg (loc);
+ else if (strcmp (directive.string, "crtl") == 0)
+ parse_crtl (loc);
+ else
+ fatal_with_file_and_line ("missing 'cfg' or 'crtl'");
+ }
+
+ apply_fixups ();
+ if (!m_have_cfg_directive)
+ recreate_implicit_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 ();
+}
+
+/* Set up state for the function *before* fixups are applied.
+
+ Create "cfun" and a decl for the function.
+ By default, every function decl is hardcoded as
+ int test_1 (int i, int j, int k);
+ Set up various other state:
+ - the cfg and basic blocks (edges are created later, *after* fixups
+ are applied).
+ - add the function to the callgraph. */
+
+void
+function_reader::create_function ()
+{
+ /* Currently we assume cfgrtl mode, rather than cfglayout mode. */
+ if (0)
+ cfg_layout_rtl_register_cfg_hooks ();
+ else
+ rtl_register_cfg_hooks ();
+
+ /* Create cfun. */
+ tree fn_name = get_identifier (m_name ? m_name : "test_1");
+ tree int_type = integer_type_node;
+ tree return_type = int_type;
+ tree arg_types[3] = {int_type, int_type, int_type};
+ tree fn_type = build_function_type_array (return_type, 3, arg_types);
+ tree fndecl = build_decl_stat (UNKNOWN_LOCATION, FUNCTION_DECL, fn_name,
+ fn_type);
+ tree resdecl = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE,
+ return_type);
+ DECL_ARTIFICIAL (resdecl) = 1;
+ DECL_IGNORED_P (resdecl) = 1;
+ DECL_RESULT (fndecl) = resdecl;
+ allocate_struct_function (fndecl, false);
+ /* This sets cfun. */
+
+ current_function_decl = fndecl;
+
+ cfun->curr_properties = (PROP_cfg | PROP_rtl);
+
+ /* Do we need this to force cgraphunit.c to output the function? */
+ DECL_EXTERNAL (fndecl) = 0;
+ DECL_PRESERVE_P (fndecl) = 1;
+
+ /* Add to cgraph. */
+ {
+ /* cgraph_node::add_new_function does additional processing
+ based on symtab->state. We need to avoid it attempting to gimplify
+ things. Temporarily putting it in the PARSING state appears to
+ achieve this. */
+ enum symtab_state old_state = symtab->state;
+ symtab->state = PARSING;
+ cgraph_node::add_new_function (fndecl, true /*lowered*/);
+ /* Reset the state. */
+ symtab->state = old_state;
+ }
+}
+
+/* Parse zero or more child insn elements within an
+ "insn-chain" element. */
+
+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);
+ parse_insn (loc, directive.string);
+ require_char_ws (')');
+ }
+ else
+ fatal_at (loc, "expected '(' or ')'");
+ }
+}
+
+/* Parse rtx instructions by calling read_rtx_code, calling
+ set_first_insn and set_last_insn as appropriate. */
+
+void
+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 = as_a <rtx_insn *> (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);
+}
+
+/* Create cfun's CFG and populate with blocks, a helper
+ function for function_reader::parse_function ().
+
+ The edges are created later on, either by parsing "edge" directives
+ in the input file, or implicity from the insns, 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_in_insns);
+ 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_in_insns, 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;
+}
+
+/* Parse a "(cfg)" directive, having parsed the "(cfg" heading.
+ Consume the final ")". */
+
+void
+function_reader::parse_cfg (file_location loc)
+{
+ if (m_have_cfg_directive)
+ error_at (loc, "more than one 'cfg' directive");
+ m_have_cfg_directive = true;
+
+ while (1)
+ {
+ int c = read_skip_spaces ();
+ file_location loc = get_current_location ();
+ if (c == ')')
+ break;
+ else if (c == '(')
+ {
+ require_word_ws ("bb");
+ parse_bb ();
+ }
+ else
+ fatal_at (loc, "expected '(' or ')'");
+ }
+
+ /* Creating the BBs is fiddly. For now, we rely on
+ create_cfg_and_basic_blocks, which works purely on the
+ set of BB indexes seen within the insns.
+ Hence we error-check to make sure the BB indexes seen
+ in the (cfg) is the same.
+
+ parse_bb complains about BB indices seen in (bb) directives that
+ weren't seen within the (insn-chain).
+
+ Complain here about the opposite: BB indices that were in the
+ (insn-chain) that were not seen in the (cfg). */
+ bitmap_head just_in_insns;
+ bitmap_initialize (&just_in_insns, &m_bitmap_obstack);
+ bitmap_and_compl (&just_in_insns, &m_bb_indices_in_insns,
+ &m_bb_indices_in_cfg);
+ unsigned bb_idx;
+ bitmap_iterator bi;
+ int num_errors = 0;
+ EXECUTE_IF_SET_IN_BITMAP (&just_in_insns, 0, bb_idx, bi)
+ /* Don't worry about entry/exit. */
+ if (bb_idx > 1)
+ {
+ error_at (loc,
+ "error: bb index %i used in (insn-chain)"
+ " but not listed in (cfg)", bb_idx);
+ num_errors++;
+ }
+ if (num_errors)
+ fatal_at (loc, "%i missing bb(s)", num_errors);
+}
+
+/* Parse a "(bb)" directive, having parsed the "(bb" heading.
+ Consume the final ")". Call parse_edge on any "(edge)"
+ directives encountered. Don't actually created basic blocks;
+ instead, verify that the BB indices correspond to
+ those seen in the insn chain. */
+
+void
+function_reader::parse_bb ()
+{
+ struct md_name name;
+ file_location loc = read_name (&name);
+ int bb_idx = atoi (name.string);
+ if (0)
+ fprintf (stderr, "parse_bb: %i\n", bb_idx);
+ bitmap_set_bit (&m_bb_indices_in_cfg, bb_idx);
+
+ /* The bb should already have been created by
+ create_cfg_and_basic_blocks. */
+ basic_block bb = BASIC_BLOCK_FOR_FN (cfun, bb_idx);
+ if (!bb)
+ fatal_at (loc, "error: bb index %i not referenced by insns", bb_idx);
+
+ /* Parse 0 or more edges. */
+ while (1)
+ {
+ int c = read_skip_spaces ();
+ file_location loc = get_current_location ();
+ if (c == ')')
+ break;
+ else if (c == '(')
+ {
+ require_word_ws ("edge");
+ parse_edge ();
+ }
+ else
+ fatal_at (loc, "expected '(' or ')'");
+ }
+}
+
+/* Parse a "(edge)" directive, having parsed the "(edge" heading.
+ Consume the final ")". Create the edge within the CFG. */
+
+void
+function_reader::parse_edge ()
+{
+ struct md_name name;
+ file_location loc = read_name (&name);
+ int src_bb_idx = atoi (name.string);
+ loc = read_name (&name);
+ int dst_bb_idx = atoi (name.string);
+
+ /* Flags. */
+ require_char_ws ('(');
+ require_word_ws ("flags");
+ struct md_name hexval;
+ read_name (&hexval);
+ int flags = strtol (hexval.string, NULL, 16);
+ require_char_ws (')');
+
+ require_char_ws (')');
+
+ if (0)
+ fprintf (stderr, "parse_edge: %i-> %i flags 0x%x \n",
+ src_bb_idx, dst_bb_idx, flags);
+
+ /* The BBs should already have been created by
+ create_cfg_and_basic_blocks. */
+ basic_block src = BASIC_BLOCK_FOR_FN (cfun, src_bb_idx);
+ if (!src)
+ fatal_at (loc, "error: bb index %i not referenced by insns", src_bb_idx);
+ basic_block dst = BASIC_BLOCK_FOR_FN (cfun, dst_bb_idx);
+ if (!dst)
+ fatal_at (loc, "error: bb index %i not referenced by insns", dst_bb_idx);
+ unchecked_make_edge (src, dst, flags);
+}
+
+/* Parse a "(crtl)" directive, having 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");
+
+ require_char_ws ('(');
+ struct md_name directive;
+ read_name (&directive);
+ crtl->return_rtx
+ = consolidate_singletons (read_rtx_code (directive.string));
+ require_char_ws (')');
+
+ require_char_ws (')');
+
+ require_char_ws (')');
+}
+
+/* Overridden implementation of rtx_reader::read_rtx_operand for
+ function_reader, handling various extra data printed by print_rtx,
+ and sometimes calling the base class implementation. */
+
+rtx
+function_reader::read_rtx_operand (rtx return_rtx, int idx)
+{
+ RTX_CODE code = GET_CODE (return_rtx);
+ const char *format_ptr = GET_RTX_FORMAT (code);
+ const char format_char = format_ptr[idx];
+ struct md_name name;
+
+ /* Override the regular parser for some format codes. */
+ switch (format_char)
+ {
+ case 'e':
+ {
+ if (idx == 7 && CALL_P (return_rtx))
+ {
+ m_in_call_function_usage = true;
+ return rtx_reader::read_rtx_operand (return_rtx, idx);
+ m_in_call_function_usage = false;
+ }
+ else
+ return rtx_reader::read_rtx_operand (return_rtx, idx);
+ }
+ break;
+
+ case 'u':
+ read_rtx_operand_u (return_rtx, idx);
+ /* Don't run regular parser for 'u'. */
+ return return_rtx;
+
+ case 'i':
+ case 'n':
+ read_rtx_operand_i_or_n (return_rtx, idx, format_char);
+ /* Don't run regular parser for these codes. */
+ return return_rtx;
+
+ case 'B':
+ {
+ file_location loc = read_name_or_nil (&name);
+ int bb_idx = atoi (name.string);
+ if (bb_idx)
+ add_fixup_bb (loc, return_rtx, idx, bb_idx);
+ /* Don't run regular parser. */
+ return return_rtx;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /* Call base class implementation. */
+ return_rtx = rtx_reader::read_rtx_operand (return_rtx, idx);
+
+ /* Handle any additional parsing needed to handle what the dump
+ could contain. */
+ switch (format_char)
+ {
+ case '0':
+ return extra_parsing_for_operand_code_0 (return_rtx, idx);
+
+ case 'w':
+ {
+ /* Strip away the redundant hex dump of the value. */
+ require_char_ws ('[');
+ read_name (&name);
+ require_char_ws (']');
+ }
+ break;
+
+ case 'r':
+ return extra_parsing_for_operand_code_r (return_rtx);
+
+ default:
+ break;
+ }
+
+ return return_rtx;
+}
+
+/* Special-cased handling of code 'u' for reading function dumps.
+
+ The RTL file recorded the ID of an insn (or 0 for NULL); we
+ must store this as a pointer, but the insn might not have
+ been loaded yet. Store the ID away for now, via a fixup. */
+
+void
+function_reader::read_rtx_operand_u (rtx return_rtx, int idx)
+{
+ struct md_name name;
+ file_location loc = read_name (&name);
+ int insn_id = atoi (name.string);
+ if (insn_id)
+ add_fixup_insn_uid (loc, return_rtx, idx, insn_id);
+}
+
+/* Special-cased handling of codes 'i' and 'n' for reading function
+ dumps. */
+
+void
+function_reader::read_rtx_operand_i_or_n (rtx return_rtx, int idx,
+ char format_char)
+{
+ /* Handle some of the extra information that print_rtx
+ can write out for these cases. */
+ /* print_rtx only writes out operand 5 for notes
+ for NOTE_KIND values NOTE_INSN_DELETED_LABEL
+ and NOTE_INSN_DELETED_DEBUG_LABEL. */
+ if (idx == 5 && NOTE_P (return_rtx))
+ return;
+
+ if (idx == 4 && INSN_P (return_rtx))
+ {
+ maybe_read_location (idx, return_rtx);
+ return;
+ }
+
+ struct md_name name;
+ read_name (&name);
+ int value;
+ if (format_char == 'n')
+ value = parse_note_insn_name (name.string);
+ else
+ value = atoi (name.string);
+ XINT (return_rtx, idx) = value;
+
+ /* 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 (idx == 5 && INSN_P (return_rtx) && format_char == 'i')
+ if (value >= 0)
+ {
+ /* Get the content within the braces, skipping the braces. */
+ require_char_ws ('{');
+ char *dumped_insn_name = read_until ("}", true);
+ if (0)
+ fprintf (stderr, "got insn name: '%s'\n", dumped_insn_name);
+ /* For now, ignore the dumped insn name. */
+ free (dumped_insn_name);
+
+ /* Reset the insn to be unrecognized. */
+ INSN_CODE (return_rtx) = -1;
+ }
+}
+
+/* Additional parsing for format code '0' in dumps, handling a variety
+ of special-cases in print_rtx. */
+
+rtx
+function_reader::extra_parsing_for_operand_code_0 (rtx return_rtx, int idx)
+{
+ RTX_CODE code = GET_CODE (return_rtx);
+ int c;
+ struct md_name name;
+
+ if (idx == 1 && code == SYMBOL_REF)
+ {
+ /* Possibly wrote " [flags %#x]", SYMBOL_REF_FLAGS (in_rtx). */
+ c = read_skip_spaces ();
+ if (c == '[')
+ {
+ file_location loc = read_name (&name);
+ if (strcmp (name.string, "flags"))
+ error_at (loc, "was expecting `%s'", "flags");
+ read_name (&name);
+ SYMBOL_REF_FLAGS (return_rtx) = strtol (name.string, NULL, 16);
+
+ /* We can't reconstruct SYMBOL_REF_BLOCK; set it to NULL. */
+ if (SYMBOL_REF_HAS_BLOCK_INFO_P (return_rtx))
+ SYMBOL_REF_BLOCK (return_rtx) = NULL;
+
+ require_char (']');
+ }
+ else
+ unread_char (c);
+
+ /* Possibly wrote:
+ print_node_brief (outfile, "", SYMBOL_REF_DECL (in_rtx),
+ dump_flags); */
+ c = read_skip_spaces ();
+ if (c == '<')
+ {
+ /* Skip the content for now. */
+ while (1)
+ {
+ char ch = read_char ();
+ if (ch == '>')
+ break;
+ }
+ }
+ else
+ unread_char (c);
+ }
+ else if (idx == 3 && code == NOTE)
+ {
+ /* Note-specific data appears for operand 3, which annoyingly
+ is before the enum specifying which kind of note we have
+ (operand 4). */
+ c = read_skip_spaces ();
+ if (c == '[')
+ {
+ /* Possibly data for a NOTE_INSN_BASIC_BLOCK, of the form:
+ [bb %d]. */
+ file_location bb_loc = read_name (&name);
+ if (strcmp (name.string, "bb"))
+ error_at (bb_loc, "was expecting `%s'", "bb");
+ read_name (&name);
+ int bb_idx = atoi (name.string);
+ add_fixup_note_insn_basic_block (bb_loc, return_rtx, idx,
+ bb_idx);
+ require_char_ws (']');
+ }
+ else
+ unread_char (c);
+ }
+ else if (idx == 7 && JUMP_P (return_rtx))
+ {
+ c = read_skip_spaces ();
+ if (c != '-')
+ {
+ unread_char (c);
+ return return_rtx;
+ }
+ require_char ('>');
+ file_location loc = read_name (&name);
+ add_fixup_jump_label (loc, return_rtx, idx, name.string);
+ }
+
+ return return_rtx;
+}
+
+/* Additional parsing for format code 'r' in dumps: register numbers.
+ The initial number has already been parsed. */
+
+rtx
+function_reader::extra_parsing_for_operand_code_r (rtx return_rtx)
+{
+ /* REGNO holds the number from the dump. */
+ unsigned int dumped_regno = REGNO (return_rtx);
+
+ /* print_rtx will print a name for hard and virtual registers
+ after the register number, and no name for pseudos.
+
+ It will then print zero, one, or two sections marked by square
+ brackets.
+
+ - see if there's a name after the number. If there is, assume a hard
+ or virtual reg, and try to parse the name:
+ - if the name is recognized, use the target's current number for that
+ name (future-proofing virtuals against .md changes)
+ - if the name is not recognized, issue a fatal error (it's probably a
+ typo, or maybe a backport from a future version of gcc, or a target
+ incompatibility)
+ - if there's no name, assume it's a pseudo. Remap all pseudos in a
+ postprocessing phase to ensure that they *are* pseudos (even if the .md
+ has gained some hard regs in the meantime), leaving the numbering
+ untouched if possible (the common case). */
+
+ int effective_regno;
+
+ int ch = read_skip_spaces ();
+ unread_char (ch);
+ if (ch != '[' && ch != ')')
+ {
+ struct md_name name;
+ file_location loc = read_name (&name);
+ int regno_by_name = lookup_reg_by_dump_name (name.string);
+ if (regno_by_name == -1)
+ fatal_at (loc, "unrecognized register: '%s'", name.string);
+ effective_regno = regno_by_name;
+ }
+ else
+ {
+ /* No reg name in dump: this was a pseudo. */
+ bitmap_set_bit (&m_dumped_pseudos, dumped_regno);
+
+ /* For now, use the dumped regno, but potentially
+ fix it up later. */
+ effective_regno = dumped_regno;
+ add_fixup_pseudo (get_current_location (), return_rtx);
+ }
+
+ set_regno_raw (return_rtx, effective_regno, 1);
+
+ /* Consolidate singletons. */
+ return_rtx = consolidate_singletons (return_rtx);
+
+ ORIGINAL_REGNO (return_rtx) = effective_regno;
+
+ /* Parse extra stuff at end of 'r'.
+ We may have zero, one, or two sections marked by square
+ brackets. */
+ ch = read_skip_spaces ();
+ bool expect_original_regno = false;
+ if (ch == '[')
+ {
+ file_location loc = get_current_location ();
+ char *desc = read_until ("]", true);
+ strip_trailing_whitespace (desc);
+ const char *desc_start = desc;
+ /* If ORIGINAL_REGNO (rtx) != regno, we will have:
+ "orig:%i", ORIGINAL_REGNO (rtx).
+ Consume it, we don't set ORIGINAL_REGNO, since we can
+ get that from the 2nd copy later. */
+ if (0 == strncmp (desc, "orig:", 5))
+ {
+ expect_original_regno = true;
+ desc_start += 5;
+ /* Skip to any whitespace following the integer. */
+ const char *space = strchr (desc_start, ' ');
+ if (space)
+ desc_start = space + 1;
+ }
+ /* Any remaining text may be the REG_EXPR. Alternatively we have
+ no REG_ATTRS, and instead we have ORIGINAL_REGNO. */
+ if (ISDIGIT (*desc_start))
+ {
+ /* Assume we have ORIGINAL_REGNO. */
+ ORIGINAL_REGNO (return_rtx) = atoi (desc_start);
+ }
+ else
+ {
+ /* Assume we have REG_EXPR. */
+ add_fixup_expr (loc, return_rtx, desc_start);
+ }
+ free (desc);
+ }
+ else
+ unread_char (ch);
+ if (expect_original_regno)
+ {
+ require_char_ws ('[');
+ char *desc = read_until ("]", true);
+ ORIGINAL_REGNO (return_rtx) = atoi (desc);
+ free (desc);
+ }
+
+ return return_rtx;
+}
+
+/* Implementation of rtx_reader::handle_any_trailing_information.
+ Handle the various additional information that print-rtl.c can
+ write after the regular fields. */
+
+void
+function_reader::handle_any_trailing_information (rtx return_rtx)
+{
+ struct md_name name;
+
+ switch (GET_CODE (return_rtx))
+ {
+ case MEM:
+ {
+ int ch;
+ require_char_ws ('[');
+ read_name (&name);
+ MEM_ALIAS_SET (return_rtx) = atoi (name.string);
+ /* We have either a MEM_EXPR, or a space. */
+ if (peek_char () != ' ')
+ {
+ file_location loc = get_current_location ();
+ char *desc = read_until (" +", false);
+ add_fixup_expr (loc, consolidate_singletons (return_rtx), desc);
+ free (desc);
+ }
+ else
+ read_char ();
+
+ /* We may optionally have '+' for MEM_OFFSET_KNOWN_P. */
+ ch = read_skip_spaces ();
+ if (ch == '+')
+ {
+ read_name (&name);
+ MEM_OFFSET_KNOWN_P (return_rtx) = 1;
+ MEM_OFFSET (return_rtx) = atoi (name.string);
+ }
+ else
+ unread_char (ch);
+
+ /* Handle optional " S" for MEM_SIZE. */
+ ch = read_skip_spaces ();
+ if (ch == 'S')
+ {
+ read_name (&name);
+ MEM_SIZE (return_rtx) = atoi (name.string);
+ }
+ else
+ unread_char (ch);
+
+ /* Handle optional " A" for MEM_ALIGN. */
+ ch = read_skip_spaces ();
+ if (ch == 'A' && peek_char () != 'S')
+ {
+ read_name (&name);
+ MEM_ALIGN (return_rtx) = atoi (name.string);
+ }
+
+ /* Handle optional " AS" for MEM_ADDR_SPACE. */
+ ch = read_skip_spaces ();
+ if (ch == 'A' && peek_char () == 'S')
+ {
+ read_char ();
+ read_name (&name);
+ MEM_ADDR_SPACE (return_rtx) = atoi (name.string);
+ }
+ else
+ unread_char (ch);
+
+ require_char (']');
+ }
+ break;
+
+ case CODE_LABEL:
+ {
+ /* 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;
+ }
+}
+
+/* Parse a tree dump for MEM_EXPR and turn it back into a tree.
+ We handle "<retval>", but for anything else we "cheat" by building a
+ global VAR_DECL of type "int" with that name (returning the same global
+ for a name if we see the same name more than once). */
+
+tree
+function_reader::parse_mem_expr (const char *desc)
+{
+ tree fndecl = cfun->decl;
+
+ if (0 == strcmp (desc, "<retval>"))
+ {
+ return DECL_RESULT (fndecl);
+ }
+
+ /* Search within function parms. */
+ for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
+ {
+ if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (arg))) == 0)
+ return arg;
+ }
+
+ /* Search within decls we already created.
+ FIXME: use a hash rather than linear search. */
+ int i;
+ tree t;
+ FOR_EACH_VEC_ELT (m_fake_scope, i, t)
+ if (strcmp (desc, IDENTIFIER_POINTER (DECL_NAME (t))) == 0)
+ return t;
+
+ /* Not found? Create it.
+ This allows mimicing of real data but avoids having to specify
+ e.g. names of locals, params etc.
+ Though this way we don't know if we have a PARM_DECL vs a VAR_DECL,
+ and we don't know the types. Fake it by making everything be
+ a VAR_DECL of "int" type. */
+ t = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+ get_identifier (desc),
+ integer_type_node);
+ m_fake_scope.safe_push (t);
+ return t;
+}
+
+/* Record the information for later post-processing. */
+
+void
+function_reader::add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx,
+ int insn_uid)
+{
+ m_fixups.safe_push (new fixup_insn_uid (loc, insn, operand_idx, insn_uid));
+}
+
+/* Record the information for later post-processing. */
+
+void
+function_reader::add_fixup_bb (file_location loc, rtx insn, int operand_idx,
+ int bb_idx)
+{
+ bitmap_set_bit (&m_bb_indices_in_insns, bb_idx);
+ m_fixups.safe_push (new fixup_bb (loc, insn, operand_idx, bb_idx));
+}
+
+/* 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_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));
+}
+
+/* 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));
+}
+
+/* Record the information for later post-processing. */
+
+void
+function_reader::add_fixup_pseudo (file_location loc, rtx x)
+{
+ m_fixups.safe_push (new fixup_pseudo (loc, x));
+}
+
+/* Helper function for consolidate_reg. */
+
+static rtx
+lookup_global_register (int regno)
+{
+ /* We can't use a switch here, as some of the REGNUMs might not be constants
+ for some targets. */
+ if (regno == STACK_POINTER_REGNUM)
+ return stack_pointer_rtx;
+ else if (regno == FRAME_POINTER_REGNUM)
+ return frame_pointer_rtx;
+ else if (regno == HARD_FRAME_POINTER_REGNUM)
+ return hard_frame_pointer_rtx;
+ else if (regno == ARG_POINTER_REGNUM)
+ return arg_pointer_rtx;
+ else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM)
+ return virtual_incoming_args_rtx;
+ else if (regno == VIRTUAL_STACK_VARS_REGNUM)
+ return virtual_stack_vars_rtx;
+ else if (regno == VIRTUAL_STACK_DYNAMIC_REGNUM)
+ return virtual_stack_dynamic_rtx;
+ else if (regno == VIRTUAL_OUTGOING_ARGS_REGNUM)
+ return virtual_outgoing_args_rtx;
+ else if (regno == VIRTUAL_CFA_REGNUM)
+ return virtual_cfa_rtx;
+ else if (regno == VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM)
+ return virtual_preferred_stack_boundary_rtx;
+#ifdef return_ADDRESS_POINTER_REGNUM
+ else if (regno == RETURN_ADDRESS_POINTER_REGNUM)
+ return return_address_pointer_rtx;
+#endif
+
+ return NULL;
+}
+
+/* Normally REG instances are created by gen_reg_rtx which updates
+ regno_reg_rtx, growing it as necessary.
+ The REG instances created from the dumpfile weren't created this
+ way, so we need to manually update regno_reg_rtx. */
+
+static void
+ensure_regno (int regno)
+{
+ crtl->emit.ensure_regno_capacity (regno + 1);
+ gcc_assert (regno < crtl->emit.regno_pointer_align_length);
+
+ if (reg_rtx_no < regno + 1)
+ reg_rtx_no = regno + 1;
+}
+
+/* Helper function for consolidate_singletons, for handling REG instances. */
+
+static rtx
+consolidate_reg (rtx x)
+{
+ gcc_assert (GET_CODE (x) == REG);
+
+ unsigned int regno = REGNO (x);
+
+ ensure_regno (regno);
+
+ /* Some register numbers have their rtx created in init_emit_regs
+ e.g. stack_pointer_rtx for STACK_POINTER_REGNUM.
+ Consolidate on this. */
+ rtx global_reg = lookup_global_register (regno);
+ if (global_reg)
+ return global_reg;
+
+ /* Populate regno_reg_rtx if necessary. */
+ if (regno_reg_rtx[regno] == NULL)
+ regno_reg_rtx[regno] = x;
+ /* Use it. */
+ gcc_assert (GET_CODE (regno_reg_rtx[regno]) == REG);
+ gcc_assert (REGNO (regno_reg_rtx[regno]) == regno);
+ if (GET_MODE (x) == GET_MODE (regno_reg_rtx[regno]))
+ return regno_reg_rtx[regno];
+
+ return x;
+}
+
+/* When reading RTL function dumps, we must consolidate some
+ rtx so that we use singletons where singletons are expected
+ (e.g. we don't want multiple "(const_int 0 [0])" rtx, since
+ these are tested via pointer equality against const0_rtx. */
+
+rtx
+function_reader::consolidate_singletons (rtx x)
+{
+ if (!x)
+ return x;
+
+ switch (GET_CODE (x))
+ {
+ /* FIXME: do we need to check for VOIDmode for these? */
+ case PC: return pc_rtx;
+ case RETURN: return ret_rtx;
+ case SIMPLE_RETURN: return simple_return_rtx;
+ case CC0: return cc0_rtx;
+
+ case REG:
+ return consolidate_reg (x);
+
+ case CONST_INT:
+ return gen_rtx_CONST_INT (GET_MODE (x), INTVAL (x));
+
+ default:
+ break;
+ }
+
+ return x;
+}
+
+/* Implementation of rtx_reader::postprocess for reading function dumps. */
+
+rtx
+function_reader::postprocess (rtx x)
+{
+ return consolidate_singletons (x);
+}
+
+/* Handle the optional location information written by print_rtx for
+ instructions. Specifically, operand 4 of instructions (of type "i')
+ is printed thus:
+
+ if (INSN_HAS_LOCATION (in_insn))
+ {
+ expanded_location xloc = insn_location (in_insn);
+ fprintf (outfile, " %s:%i", xloc.file, xloc.line);
+ }
+
+ Hence we need to speculatively read a location of the form
+ " %s:%i", and unread the content if there wasn't one.
+
+ Assume that filenames can't contain whitespace, and can't
+ contain ':'. */
+
+void
+function_reader::maybe_read_location (int operand_idx, rtx insn)
+{
+ file_location loc = get_current_location ();
+
+ /* Skip to first non-whitespace. */
+ int ch = read_skip_spaces ();
+ auto_vec<char> buf;
+ buf.safe_push (ch);
+ while (1)
+ {
+ int ch = read_char ();
+ /* If we see a ':', assume we have a filename. */
+ if (ch == ':')
+ {
+ buf.safe_push ('\0');
+ break;
+ }
+ buf.safe_push (ch);
+
+ /* If we see a space before ':', assume we don't have a
+ filename. */
+ if (ISSPACE (ch))
+ {
+ while (!buf.is_empty ())
+ unread_char (buf.pop ());
+ return;
+ }
+ }
+ char *filename = buf.address ();
+ struct md_name name;
+ read_name (&name);
+
+ add_fixup_source_location (loc, insn, operand_idx,
+ filename, atoi(name.string));
+}
+
+/* Apply all of the recorded fixups. */
+
+void
+function_reader::apply_fixups ()
+{
+ /* Set up m_pseudo_offset so that the lowest REGNO seen for a pseudo in
+ the dump will be a pseudo after any fixup_pseudo instances are applied,
+ preferring to leave it as zero if possible (to avoid renumbering). */
+ if (!bitmap_empty_p (&m_dumped_pseudos))
+ {
+ int lowest_pseudo_in_dump = bitmap_first_set_bit (&m_dumped_pseudos);
+ if (lowest_pseudo_in_dump < LAST_VIRTUAL_REGISTER + 1)
+ {
+ m_pseudo_offset = (LAST_VIRTUAL_REGISTER + 1) - lowest_pseudo_in_dump;
+
+ /* We may need to resize the regno-based arrays. */
+ int highest_pseudo_in_dump = bitmap_last_set_bit (&m_dumped_pseudos);
+ int highest_effective_pseudo = highest_pseudo_in_dump + m_pseudo_offset;
+ ensure_regno (highest_effective_pseudo);
+ }
+ }
+
+ 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);
+}
+
+/* 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 <rtx_insn *> (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::recreate_implicit_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 <rtx_jump_insn *> (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);
+ }
+ }
+ }
+}
+
+/* Run the RTL dump parser. If OUT_PSEUDO_OFFSET is non-NULL,
+ write any offset applied to the regnos of pseudoes to
+ *OUT_PSEUDO_OFFSET. */
+
+bool
+read_rtl_function_body (int argc, const char **argv,
+ bool (*parse_opt) (const char *),
+ function_reader_policy *policy,
+ int *out_pseudo_offset)
+{
+ initialize_rtl ();
+ init_emit ();
+ init_varasm_status ();
+
+ function_reader reader (policy);
+ if (!reader.read_md_files (argc, argv, parse_opt))
+ return false;
+
+ if (out_pseudo_offset)
+ *out_pseudo_offset = reader.get_pseudo_offset ();
+
+ return true;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that the src and dest indices and flags of edge E are as
+ expected, using LOC as the effective location when reporting
+ failures. */
+
+static void
+assert_edge_at (const location &loc, edge e, int expected_src_idx,
+ int expected_dest_idx, int expected_flags)
+{
+ ASSERT_EQ_AT (loc, expected_src_idx, e->src->index);
+ ASSERT_EQ_AT (loc, expected_dest_idx, e->dest->index);
+ ASSERT_EQ_AT (loc, expected_flags, e->flags);
+}
+
+/* Verify that the src and dest indices and flags of EDGE are as
+ expected. */
+
+#define ASSERT_EDGE(EDGE, EXPECTED_SRC_IDX, EXPECTED_DEST_IDX, \
+ EXPECTED_FLAGS) \
+ assert_edge_at (SELFTEST_LOCATION, EDGE, EXPECTED_SRC_IDX, \
+ EXPECTED_DEST_IDX, EXPECTED_FLAGS)
+
+/* Verify that we can load RTL dumps. */
+
+static void
+test_loading_dump_fragment_1 ()
+{
+ // TODO: filter on target?
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("asr_div1.rtl"));
+
+ /* Verify that the insns were loaded correctly. */
+ rtx_insn *insn_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, with the implicit edges being
+ recreated. */
+ ASSERT_TRUE (cfun);
+ verify_three_block_rtl_cfg (cfun);
+ basic_block bb2 = BASIC_BLOCK_FOR_FN (cfun, 2);
+ ASSERT_TRUE (bb2 != NULL);
+ ASSERT_EQ (BB_RTL, bb2->flags & BB_RTL);
+ ASSERT_EQ (2, bb2->index);
+ ASSERT_EQ (insn_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 ()
+{
+ /* Only run this test for i386, since it hardcodes a hard reg name. */
+#ifndef I386_OPTS_H
+ return;
+#endif
+
+ rtl_dump_test t (SELFTEST_LOCATION,
+ locate_file ("copy-hard-reg-into-frame.rtl"));
+
+ 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 ()
+{
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("example-labels.rtl"));
+
+ 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 ()
+{
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("insn-with-mode.rtl"));
+ 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 ()
+{
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("jump-to-label-ref.rtl"));
+
+ rtx_insn *jump_insn = get_insn_by_uid (1);
+ ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
+
+ rtx_insn *barrier = get_insn_by_uid (2);
+ ASSERT_EQ (BARRIER, GET_CODE (barrier));
+
+ rtx_insn *code_label = get_insn_by_uid (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));
+
+ /* Verify the generated CFG. */
+
+ /* Locate blocks. */
+ basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun);
+ ASSERT_TRUE (entry != NULL);
+ ASSERT_EQ (ENTRY_BLOCK, entry->index);
+
+ basic_block exit = EXIT_BLOCK_PTR_FOR_FN (cfun);
+ ASSERT_TRUE (exit != NULL);
+ ASSERT_EQ (EXIT_BLOCK, exit->index);
+
+ basic_block bb4 = (*cfun->cfg->x_basic_block_info)[4];
+ basic_block bb5 = (*cfun->cfg->x_basic_block_info)[5];
+ ASSERT_EQ (4, bb4->index);
+ ASSERT_EQ (5, bb5->index);
+
+ /* Entry block. */
+ ASSERT_EQ (NULL, entry->preds);
+ ASSERT_EQ (1, entry->succs->length ());
+ ASSERT_EDGE ((*entry->succs)[0], 0, 4, EDGE_FALLTHRU);
+
+ /* bb4. */
+ ASSERT_EQ (1, bb4->preds->length ());
+ ASSERT_EDGE ((*bb4->preds)[0], 0, 4, EDGE_FALLTHRU);
+ ASSERT_EQ (1, bb4->succs->length ());
+ ASSERT_EDGE ((*bb4->succs)[0], 4, 5, 0x0);
+
+ /* bb5. */
+ ASSERT_EQ (1, bb5->preds->length ());
+ ASSERT_EDGE ((*bb5->preds)[0], 4, 5, 0x0);
+ ASSERT_EQ (1, bb5->succs->length ());
+ ASSERT_EDGE ((*bb5->succs)[0], 5, 1, EDGE_FALLTHRU);
+
+ /* Exit block. */
+ ASSERT_EQ (1, exit->preds->length ());
+ ASSERT_EDGE ((*exit->preds)[0], 5, 1, EDGE_FALLTHRU);
+ ASSERT_EQ (NULL, exit->succs);
+}
+
+/* Verify that the loader copes with a jump_insn to a label_ref
+ marked "return". */
+
+static void
+test_loading_jump_to_return ()
+{
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("jump-to-return.rtl"));
+
+ rtx_insn *jump_insn = get_insn_by_uid (1);
+ ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
+ ASSERT_EQ (ret_rtx, JUMP_LABEL (jump_insn));
+ // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^
+}
+
+/* Verify that the loader copes with a jump_insn to a label_ref
+ marked "simple_return". */
+
+static void
+test_loading_jump_to_simple_return ()
+{
+ rtl_dump_test t (SELFTEST_LOCATION,
+ locate_file ("jump-to-simple-return.rtl"));
+
+ rtx_insn *jump_insn = get_insn_by_uid (1);
+ ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
+ ASSERT_EQ (simple_return_rtx, JUMP_LABEL (jump_insn));
+ // FIXME: ^^^ use ASSERT_RTX_PTR_EQ here ^^^
+}
+
+/* Verify that the loader copes with a NOTE_INSN_BASIC_BLOCK. */
+
+static void
+test_loading_note_insn_basic_block ()
+{
+ rtl_dump_test t (SELFTEST_LOCATION,
+ locate_file ("note_insn_basic_block.rtl"));
+
+ rtx_insn *note = get_insn_by_uid (1);
+ ASSERT_EQ (NOTE, GET_CODE (note));
+ ASSERT_EQ (2, BLOCK_FOR_INSN (note)->index);
+
+ ASSERT_EQ (NOTE_INSN_BASIC_BLOCK, NOTE_KIND (note));
+ ASSERT_EQ (2, NOTE_BASIC_BLOCK (note)->index);
+ ASSERT_EQ (BASIC_BLOCK_FOR_FN (cfun, 2), NOTE_BASIC_BLOCK (note));
+}
+
+/* Verify that the loader copes with a NOTE_INSN_DELETED. */
+
+static void
+test_loading_note_insn_deleted ()
+{
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("note-insn-deleted.rtl"));
+
+ rtx_insn *note = get_insn_by_uid (1);
+ ASSERT_EQ (NOTE, GET_CODE (note));
+ ASSERT_EQ (NOTE_INSN_DELETED, NOTE_KIND (note));
+}
+
+/* Verify that the const_int values are consolidated, since
+ pointer equality corresponds to value equality.
+ TODO: do this for all in CASE_CONST_UNIQUE. */
+
+static void
+test_loading_const_int ()
+{
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("const-int.rtl"));
+
+ /* Verify that const_int values below MAX_SAVED_CONST_INT use
+ the global values. */
+ ASSERT_EQ (const0_rtx, SET_SRC (PATTERN (get_insn_by_uid (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 ()
+{
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("symbol-ref.rtl"));
+
+ 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. */
+}
+
+/* Verify that the loader copes with a call_insn dump. */
+
+static void
+test_loading_call_insn_x86_64 ()
+{
+ /* Only run this test for i386. */
+#ifndef I386_OPTS_H
+ return;
+#endif
+
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("x86_64/call-insn.rtl"));
+
+ rtx_insn *insn_17 = get_insn_by_uid (17);
+ ASSERT_EQ (CALL_INSN, GET_CODE (insn_17));
+
+ /* "/j". */
+ ASSERT_TRUE (RTX_FLAG (insn_17, jump));
+
+ rtx pat = PATTERN (insn_17);
+ ASSERT_EQ (CALL, GET_CODE (SET_SRC (pat)));
+
+ /* Verify REG_NOTES. */
+ {
+ /* "(expr_list:REG_CALL_DECL". */
+ ASSERT_EQ (EXPR_LIST, GET_CODE (REG_NOTES (insn_17)));
+ rtx_expr_list *note0 = as_a <rtx_expr_list *> (REG_NOTES (insn_17));
+ ASSERT_EQ (REG_CALL_DECL, REG_NOTE_KIND (note0));
+
+ /* "(expr_list:REG_EH_REGION (const_int 0 [0])". */
+ rtx_expr_list *note1 = note0->next ();
+ ASSERT_EQ (REG_EH_REGION, REG_NOTE_KIND (note1));
+
+ ASSERT_EQ (NULL, note1->next ());
+ }
+
+ /* Verify CALL_INSN_FUNCTION_USAGE. */
+ {
+ /* "(expr_list:DF (use (reg:DF 21 xmm0))". */
+ rtx_expr_list *usage
+ = as_a <rtx_expr_list *> (CALL_INSN_FUNCTION_USAGE (insn_17));
+ ASSERT_EQ (EXPR_LIST, GET_CODE (usage));
+ ASSERT_EQ (DFmode, GET_MODE (usage));
+ ASSERT_EQ (USE, GET_CODE (usage->element ()));
+ ASSERT_EQ (NULL, usage->next ());
+ }
+}
+
+/* Verify that the loader copes a dump from print_rtx_function. */
+
+static void
+test_loading_full_dump_aarch64 ()
+{
+ /* Only run this test for target==aarch64. */
+#ifndef GCC_AARCH64_H
+ return;
+#endif
+
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("aarch64/times-two.rtl"));
+
+ ASSERT_STREQ ("times_two", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+
+ rtx_insn *insn_1 = get_insn_by_uid (1);
+ ASSERT_EQ (NOTE, GET_CODE (insn_1));
+
+ rtx_insn *insn_15 = get_insn_by_uid (15);
+ ASSERT_EQ (INSN, GET_CODE (insn_15));
+ ASSERT_EQ (USE, GET_CODE (PATTERN (insn_15)));
+
+ /* Verify crtl->return_rtx. */
+ ASSERT_EQ (REG, GET_CODE (crtl->return_rtx));
+ ASSERT_EQ (0, REGNO (crtl->return_rtx));
+ ASSERT_EQ (SImode, GET_MODE (crtl->return_rtx));
+}
+
+/* Verify that the loader copes a dump from print_rtx_function. */
+
+static void
+test_loading_full_dump_x86_64 ()
+{
+ /* Only run this test for i386. */
+#ifndef I386_OPTS_H
+ return;
+#endif
+
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("x86_64/times-two.rtl"));
+
+ ASSERT_STREQ ("times_two", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+
+ rtx_insn *insn_1 = get_insn_by_uid (1);
+ ASSERT_EQ (NOTE, GET_CODE (insn_1));
+
+ rtx_insn *insn_7 = get_insn_by_uid (7);
+ ASSERT_EQ (INSN, GET_CODE (insn_7));
+ ASSERT_EQ (PARALLEL, GET_CODE (PATTERN (insn_7)));
+
+ rtx_insn *insn_15 = get_insn_by_uid (15);
+ ASSERT_EQ (INSN, GET_CODE (insn_15));
+ ASSERT_EQ (USE, GET_CODE (PATTERN (insn_15)));
+
+ /* Verify crtl->return_rtx. */
+ ASSERT_EQ (REG, GET_CODE (crtl->return_rtx));
+ ASSERT_EQ (0, REGNO (crtl->return_rtx));
+ ASSERT_EQ (SImode, GET_MODE (crtl->return_rtx));
+}
+
+/* Verify that the loader can rebuild a CFG. */
+
+static void
+test_loading_cfg ()
+{
+ rtl_dump_test t (SELFTEST_LOCATION, locate_file ("cfg-test.rtl"));
+
+ ASSERT_STREQ ("cfg_test", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
+
+ ASSERT_TRUE (cfun);
+
+ ASSERT_TRUE (cfun->cfg != NULL);
+ ASSERT_EQ (6, n_basic_blocks_for_fn (cfun));
+ ASSERT_EQ (6, n_edges_for_fn (cfun));
+
+ /* The "fake" basic blocks. */
+ basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun);
+ ASSERT_TRUE (entry != NULL);
+ ASSERT_EQ (ENTRY_BLOCK, entry->index);
+
+ basic_block exit = EXIT_BLOCK_PTR_FOR_FN (cfun);
+ ASSERT_TRUE (exit != NULL);
+ ASSERT_EQ (EXIT_BLOCK, exit->index);
+
+ /* The "real" basic blocks. */
+ basic_block bb2 = (*cfun->cfg->x_basic_block_info)[2];
+ basic_block bb3 = (*cfun->cfg->x_basic_block_info)[3];
+ basic_block bb4 = (*cfun->cfg->x_basic_block_info)[4];
+ basic_block bb5 = (*cfun->cfg->x_basic_block_info)[5];
+
+ ASSERT_EQ (2, bb2->index);
+ ASSERT_EQ (3, bb3->index);
+ ASSERT_EQ (4, bb4->index);
+ ASSERT_EQ (5, bb5->index);
+
+ /* Verify connectivity. */
+
+ /* Entry block. */
+ ASSERT_EQ (NULL, entry->preds);
+ ASSERT_EQ (1, entry->succs->length ());
+ ASSERT_EDGE ((*entry->succs)[0], 0, 2, 0x1);
+
+ /* bb2. */
+ ASSERT_EQ (1, bb2->preds->length ());
+ ASSERT_EDGE ((*bb2->preds)[0], 0, 2, 0x1);
+ ASSERT_EQ (2, bb2->succs->length ());
+ ASSERT_EDGE ((*bb2->succs)[0], 2, 3, 0x1);
+ ASSERT_EDGE ((*bb2->succs)[1], 2, 4, 0x1);
+
+ /* bb3. */
+ ASSERT_EQ (1, bb3->preds->length ());
+ ASSERT_EDGE ((*bb3->preds)[0], 2, 3, 0x1);
+ ASSERT_EQ (1, bb3->succs->length ());
+ ASSERT_EDGE ((*bb3->succs)[0], 3, 5, 0x1);
+
+ /* bb4. */
+ ASSERT_EQ (1, bb4->preds->length ());
+ ASSERT_EDGE ((*bb4->preds)[0], 2, 4, 0x1);
+ ASSERT_EQ (1, bb4->succs->length ());
+ ASSERT_EDGE ((*bb4->succs)[0], 4, 5, 0x1);
+
+ /* bb5. */
+ ASSERT_EQ (2, bb5->preds->length ());
+ ASSERT_EDGE ((*bb5->preds)[0], 3, 5, 0x1);
+ ASSERT_EDGE ((*bb5->preds)[1], 4, 5, 0x1);
+ ASSERT_EQ (1, bb5->succs->length ());
+ ASSERT_EDGE ((*bb5->succs)[0], 5, 1, 0x1);
+
+ /* Exit block. */
+ ASSERT_EQ (1, exit->preds->length ());
+ ASSERT_EDGE ((*exit->preds)[0], 5, 1, 0x1);
+ ASSERT_EQ (NULL, exit->succs);
+}
+
+/* Verify that renumbering pseudos works. */
+
+static void
+test_regno_renumbering ()
+{
+ rtl_dump_test t (SELFTEST_LOCATION,
+ locate_file ("test-regno-renumbering.rtl"));
+
+ rtx_insn *insn_1 = get_insn_by_uid (1);
+ rtx set = single_set (insn_1);
+ ASSERT_NE (NULL, set);
+ rtx dst = SET_DEST (set);
+ rtx src = SET_SRC (set);
+ /* The registers did not have names, and so should have been treated
+ as pseudos.
+ Verify that they have been renumbered, with the lower one
+ having LAST_VIRTUAL_REGISTER + 1. */
+ ASSERT_EQ (LAST_VIRTUAL_REGISTER + 1, REGNO (dst));
+ ASSERT_EQ (LAST_VIRTUAL_REGISTER + 2, REGNO (src));
+}
+
+/* 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 ();
+ test_loading_call_insn_x86_64 ();
+ test_loading_full_dump_aarch64 ();
+ test_loading_full_dump_x86_64 ();
+ test_loading_cfg ();
+ test_regno_renumbering ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
new file mode 100644
@@ -0,0 +1,37 @@
+/* read-rtl-function.h - Reader for RTL function dumps
+ Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_READ_RTL_FUNCTION_H
+#define GCC_READ_RTL_FUNCTION_H
+
+/* An optional policy class for class function_reader. */
+
+struct function_reader_policy
+{
+ function_reader_policy ()
+ {
+ }
+};
+
+extern bool read_rtl_function_body (int argc, const char **argv,
+ bool (*parse_opt) (const char *),
+ function_reader_policy *policy,
+ int *out_pseudo_offset);
+
+#endif /* GCC_READ_RTL_FUNCTION_H */
@@ -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,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,6 +117,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;
@@ -181,6 +193,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 +266,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 +434,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 +599,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 +674,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 +745,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 +788,7 @@ read_conditions (void)
add_c_test (expr, value);
}
}
+#endif /* #ifdef GENERATOR_FILE */
static void
validate_const_int (const char *string)
@@ -861,6 +885,8 @@ record_potential_iterator_use (struct iterator_group *group, void *ptr,
}
}
+#ifdef GENERATOR_FILE
+
/* Finish reading a declaration of the form:
(define... <name> [<value1> ... <valuen>])
@@ -1020,14 +1046,7 @@ check_code_iterator (struct mapping *iterator)
bool
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 +1101,103 @@ 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,7 +1206,7 @@ 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;
@@ -1103,13 +1219,19 @@ rtx_reader::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);
@@ -1120,6 +1242,26 @@ rtx_reader::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. */
+ 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 +1274,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))
{
@@ -1217,6 +1375,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;
@@ -1292,7 +1453,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
@@ -1347,11 +1511,23 @@ rtx_reader::read_rtx_operand (rtx return_rtx, int idx)
if (m != 0)
record_iterator_use (m, return_rtx);
}
+#endif /* #ifdef GENERATOR_FILE */
+
+ /* "stringbuf" was allocated within string_obstack and thus has
+ the its lifetime restricted to that of the rtx_reader. This is
+ OK for the generator programs, but for non-generator programs,
+ XSTR and XTMPL fields are meant to be allocated in the GC-managed
+ heap. Hence we need to allocate a copy in the GC-managed heap
+ for the non-generator case. */
+ const char *string_ptr = stringbuf;
+#ifndef GENERATOR_FILE
+ string_ptr = ggc_strdup (stringbuf);
+#endif /* #ifndef GENERATOR_FILE */
if (star_if_braced)
- XTMPL (return_rtx, idx) = stringbuf;
+ XTMPL (return_rtx, idx) = string_ptr;
else
- XSTR (return_rtx, idx) = stringbuf;
+ XSTR (return_rtx, idx) = string_ptr;
}
break;
@@ -1419,6 +1595,8 @@ rtx_reader::read_nested_rtx (void)
require_char_ws (')');
+ return_rtx = postprocess (return_rtx);
+
return return_rtx;
}
@@ -1456,7 +1634,8 @@ rtx_reader::read_rtx_variadic (rtx form)
/* Constructor for class rtx_reader. */
rtx_reader::rtx_reader ()
-: base_rtx_reader ()
+: base_rtx_reader (),
+ m_in_call_function_usage (false)
{
/* Set the global singleton pointer. */
rtx_reader_ptr = this;
@@ -3641,7 +3641,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);
new file mode 100644
@@ -0,0 +1,90 @@
+/* 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 "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), m_pseudo_offset (0)
+{
+ /* Parse the tempfile. */
+ auto_vec<const char *> argv (2);
+ argv.safe_push (progname);
+ argv.safe_push (path);
+ bool read_ok
+ = read_rtl_function_body (argv.length (), argv.address (), NULL, NULL,
+ &m_pseudo_offset);
+ 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);
+}
+
+/* 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 input_regno + m_pseudo_offset;
+}
+
+/* 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 */
new file mode 100644
@@ -0,0 +1,67 @@
+/* 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
+<http://www.gnu.org/licenses/>. */
+
+#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-rtl-function.h"
+
+namespace selftest {
+
+/* 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 ();
+
+ unsigned int effective_regno (int input_regno) const;
+
+ private:
+ char *m_path;
+ int m_pseudo_offset;
+};
+
+/* Wrapper class for initializing/cleaning up df. */
+
+class dataflow_test
+{
+ public:
+ dataflow_test ();
+ ~dataflow_test ();
+};
+
+/* Get the insn with the given uid, or NULL if not found. */
+
+extern rtx_insn *get_insn_by_uid (int uid);
+
+extern void verify_three_block_rtl_cfg (function *fun);
+
+} /* end of namespace selftest. */
+
+#endif /* #if CHECKING_P */
+
+#endif /* GCC_SELFTEST_RTL_H */
@@ -70,6 +70,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. */
@@ -213,6 +213,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 ();
new file mode 100644
@@ -0,0 +1,42 @@
+(function "times_two"
+ (insn-chain
+ (note 1 0 4 (nil) NOTE_INSN_DELETED)
+ (note 4 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
+ (insn 2 4 3 2 (set (mem/c:SI (plus:DI (reg/f:DI 68 virtual-stack-vars)
+ (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])
+ (reg:SI 0 x0 [ i ])) times-two.c:2 -1
+ (nil))
+ (note 3 2 6 2 NOTE_INSN_FUNCTION_BEG)
+ (insn 6 3 7 2 (set (reg:SI 75)
+ (mem/c:SI (plus:DI (reg/f:DI 68 virtual-stack-vars)
+ (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])) times-two.c:3 -1
+ (nil))
+ (insn 7 6 10 2 (set (reg:SI 73 [ _2 ])
+ (ashift:SI (reg:SI 75)
+ (const_int 1 [0x1]))) times-two.c:3 -1
+ (nil))
+ (insn 10 7 14 2 (set (reg:SI 74 [ <retval> ])
+ (reg:SI 73 [ _2 ])) times-two.c:3 -1
+ (nil))
+ (insn 14 10 15 2 (set (reg/i:SI 0 x0)
+ (reg:SI 74 [ <retval> ])) times-two.c:4 -1
+ (nil))
+ (insn 15 14 0 2 (use (reg/i:SI 0 x0)) times-two.c:4 -1
+ (nil))
+ ) ;; insn-chain
+ (cfg
+ (bb 0
+ (edge 0 2 (flags 0x1))
+ ) ;; bb
+ (bb 2
+ (edge 2 1 (flags 0x1))
+ ) ;; bb
+ (bb 1
+ ) ;; bb
+ ) ;; cfg
+ (crtl
+ (return_rtx
+ (reg/i:SI 0 x0)
+ ) ;; return_rtx
+ ) ;; crtl
+) ;; function "times_two"
new file mode 100644
@@ -0,0 +1,23 @@
+;; 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. */
+
+(function "f1"
+ (insn-chain
+(insn 8 0 9 2 (set (reg:DI 78)
+ (lshiftrt:DI (reg:DI 76)
+ (const_int 32 [0x20])))
+ ../../src/gcc/testsuite/gcc.dg/asr_div1.c:14
+ 641 {*aarch64_lshr_sisd_or_int_di3}
+ (expr_list:REG_DEAD (reg:DI 76)
+ (nil)))
+(insn 9 8 0 2 (set (reg:SI 79)
+ (ashiftrt:SI (subreg:SI (reg:DI 78) 0)
+ (const_int 3 [0x3])))
+ ../../src/gcc/testsuite/gcc.dg/asr_div1.c:14
+ 642 {*aarch64_ashr_sisd_or_int_si3}
+ (expr_list:REG_DEAD (reg:DI 78)
+ (nil)))
+ ) ;; insn-chain
+) ;; function
new file mode 100644
@@ -0,0 +1,38 @@
+/* Example of a loading a CFG like this:
+ 0 (entry)
+ |
+ 2
+ / \
+ 3 4
+ \ /
+ 5
+ |
+ 1 (exit). */
+
+(function "cfg_test"
+ (insn-chain
+ (note 2 0 3 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
+ (note 3 2 4 3 [bb 3] NOTE_INSN_BASIC_BLOCK)
+ (note 4 3 5 4 [bb 4] NOTE_INSN_BASIC_BLOCK)
+ (note 5 4 0 5 [bb 5] NOTE_INSN_BASIC_BLOCK)
+ ) ;; empty
+ (cfg
+ (bb 0 ;; entry
+ (edge 0 2 (flags 0x1))
+ ) ;; bb
+ (bb 2
+ (edge 2 3 (flags 0x1))
+ (edge 2 4 (flags 0x1))
+ ) ;; bb
+ (bb 3
+ (edge 3 5 (flags 0x1))
+ ) ;; bb
+ (bb 4
+ (edge 4 5 (flags 0x1))
+ ) ;; bb
+ (bb 5
+ (edge 5 1 (flags 0x1))
+ ) ;; bb
+ (bb 1) ;; exit
+ ) ;; cfg
+)
new file mode 100644
@@ -0,0 +1,16 @@
+(function "const_int_examples"
+ (insn-chain
+ (insn 100 0 101 2
+ (set (reg:SI 100) (const_int 0 [0x0]))
+ test.c:2 -1 (nil))
+ (insn 101 100 102 2
+ (set (reg:SI 101) (const_int 1 [0x1]))
+ test.c:2 -1 (nil))
+ (insn 102 101 103 2
+ (set (reg:SI 102) (const_int -1 [0xffffffff]))
+ test.c:2 -1 (nil))
+ (insn 103 102 0 2
+ (set (reg:SI 103) (const_int 256 [0x100]))
+ test.c:2 -1 (nil))
+ ) ;; insn-chain
+) ;; function
new file mode 100644
@@ -0,0 +1,12 @@
+(function "copy_hard_reg_into_frame"
+ (insn-chain
+ (insn 2 0 0 2
+ (set (mem/c:SI
+ (plus:DI
+ (reg/f:DI 20 frame)
+ (const_int -4 [0xfffffffffffffffc]))
+ [1 i+0 S4 A32])
+ (reg:SI 5 di [ i ])) test.c:2 -1
+ (nil))
+ ) ;; insn-chain
+) ;; function
new file mode 100644
@@ -0,0 +1,6 @@
+(function "example_labels"
+ (insn-chain
+ (code_label 1 0 2 6 3 (nil) [0 uses])
+ (code_label 2 1 0 6 3 ("some_label_name") [57 uses])
+ ) ;; insn-chain
+) ;; function
new file mode 100644
@@ -0,0 +1,5 @@
+(function "insn_with_mode"
+ (insn-chain
+ (insn:TI 31 0 0 2 (set (reg:SI 100) (reg:SI 101)) -1 (nil))
+ ) ;; insn-chain
+) ;; function
new file mode 100644
@@ -0,0 +1,11 @@
+(function "jump_to_label_ref"
+ (insn-chain
+ (jump_insn 1 0 2 4 (set (pc)
+ (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1
+ (nil)
+ -> 3)
+ (barrier 2 1 3)
+ (code_label 3 2 0 5 2 (nil) [1 uses])
+ ) ;; insn-chain
+) ;; function
+
new file mode 100644
@@ -0,0 +1,10 @@
+(function "jump_to_return"
+ (insn-chain
+ (jump_insn 1 0 2 4 (set (pc)
+ (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1
+ (nil)
+ -> return)
+ (barrier 2 1 3)
+ (code_label 3 2 0 5 2 (nil) [1 uses])
+ ) ;; insn-chain
+) ;; function
new file mode 100644
@@ -0,0 +1,11 @@
+(function "jump_to_simple_return"
+ (insn-chain
+ (jump_insn 1 0 2 4 (set (pc)
+ (label_ref 3)) ../../src/gcc/testsuite/rtl.dg/test.c:4 -1
+ (nil)
+ -> simple_return)
+ (barrier 2 1 3)
+ (code_label 3 2 0 5 2 (nil) [1 uses])
+ ) ;; insn-chain
+) ;; function
+
new file mode 100644
@@ -0,0 +1,5 @@
+(function "example_note"
+ (insn-chain
+ (note 1 0 0 (nil) NOTE_INSN_DELETED)
+ ) ;; insn-chain
+) ;; function
new file mode 100644
@@ -0,0 +1,5 @@
+(function "example_of_note"
+ (insn-chain
+ (note 1 0 0 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
+ ) ;; insn-chain
+) ;; function
new file mode 100644
@@ -0,0 +1,7 @@
+(function "example_of_symbol_ref"
+ (insn-chain
+ (insn 1045 0 0 2 (set (reg:SI 480)
+ (high:SI (symbol_ref:SI ("isl_obj_map_vtable") [flags 0xc0] <var_decl 0x7fa0363ea240 isl_obj_map_vtable>))) y.c:12702 -1
+ (nil))
+ ) ;; insn-chain
+) ;; function
new file mode 100644
@@ -0,0 +1,6 @@
+(function "test"
+ (insn-chain
+ ;; The regs here have no names and are thus to be treated as pseudos.
+ (insn 1 0 0 2 (set (reg:SI 10) (reg:SI 11)) -1 (nil))
+ ) ;; insn-chain
+) ;; function
new file mode 100644
@@ -0,0 +1,12 @@
+(function "test"
+ (insn-chain
+ (call_insn/j 17 0 0 2 (set (reg:DF 21 xmm0)
+ (call (mem:QI (symbol_ref:DI ("sqrt") [flags 0x41] <function_decl 0x7f82b1429d00 sqrt>) [0 __builtin_sqrt S1 A8])
+ (const_int 0 [0]))) test.c:19 -1
+ (expr_list:REG_CALL_DECL (symbol_ref:DI ("sqrt") [flags 0x41] <function_decl 0x7f82b1429d00 sqrt>)
+ (expr_list:REG_EH_REGION (const_int 0 [0])
+ (nil)))
+ (expr_list:DF (use (reg:DF 21 xmm0))
+ (nil)))
+ ) ;; insn-chain
+) ;; function "test"
new file mode 100644
@@ -0,0 +1,57 @@
+;; Dump of this C function:
+;;
+;; int times_two (int i)
+;; {
+;; return i * 2;
+;; }
+;;
+;; after expand for target==x86_64
+
+(function "times_two"
+ (insn-chain
+ (note 1 0 4 (nil) NOTE_INSN_DELETED)
+ (note 4 1 2 2 [bb 2] NOTE_INSN_BASIC_BLOCK)
+ (insn 2 4 3 2 (set (mem/c:SI (plus:DI (reg/f:DI 82 virtual-stack-vars)
+ (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])
+ (reg:SI 5 di [ i ])) t.c:2 -1
+ (nil))
+ (note 3 2 6 2 NOTE_INSN_FUNCTION_BEG)
+ (insn 6 3 7 2 (set (reg:SI 89)
+ (mem/c:SI (plus:DI (reg/f:DI 82 virtual-stack-vars)
+ (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])) t.c:3 -1
+ (nil))
+ (insn 7 6 10 2 (parallel [
+ (set (reg:SI 87 [ _2 ])
+ (ashift:SI (reg:SI 89)
+ (const_int 1 [0x1])))
+ (clobber (reg:CC 17 flags))
+ ]) t.c:3 -1
+ (expr_list:REG_EQUAL (ashift:SI (mem/c:SI (plus:DI (reg/f:DI 82 virtual-stack-vars)
+ (const_int -4 [0xfffffffffffffffc])) [1 i+0 S4 A32])
+ (const_int 1 [0x1]))
+ (nil)))
+ (insn 10 7 14 2 (set (reg:SI 88 [ <retval> ])
+ (reg:SI 87 [ _2 ])) t.c:3 -1
+ (nil))
+ (insn 14 10 15 2 (set (reg/i:SI 0 ax)
+ (reg:SI 88 [ <retval> ])) t.c:4 -1
+ (nil))
+ (insn 15 14 0 2 (use (reg/i:SI 0 ax)) t.c:4 -1
+ (nil))
+ ) ;; insn-chain
+ (cfg
+ (bb 0
+ (edge 0 2 (flags 0x1))
+ ) ;; bb
+ (bb 2
+ (edge 2 1 (flags 0x1))
+ ) ;; bb
+ (bb 1
+ ) ;; bb
+ ) ;; cfg
+ (crtl
+ (return_rtx
+ (reg/i:SI 0 ax)
+ ) ;; return_rtx
+ ) ;; crtl
+) ;; function "times_two"
@@ -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));