8sa1-gcc/gcc/read-rtl-function.c
Jakub Jelinek 700d4cb08c Fix up duplicated duplicated words mostly in comments
In the r10-7197-gbae7b38cf8a21e068ad5c0bab089dedb78af3346 commit I've
noticed duplicated word in a message, which lead me to grep for those and
we have a tons of them.
I've used
grep -v 'long long\|optab optab\|template template\|double double' *.[chS] */*.[chS] *.def config/*/* 2>/dev/null | grep ' \([a-zA-Z]\+\) \1 '
Note, the command will not detect the doubled words at the start or end of
line or when one of the words is at the end of line and the next one at the
start of another one.
Some of it is fairly obvious, e.g. all the "the the" cases which is
something I've posted and committed patch for already e.g. in 2016,
other cases are often valid, e.g. "that that" seems to look mostly ok to me.
Some cases are quite hard to figure out, I've left out some of them from the
patch (e.g. "and and" in some cases isn't talking about bitwise/logical and
and so looks incorrect, but in other cases it is talking about those
operations).
In most cases the right solution seems to be to remove one of the duplicated
words, but not always.

I think most important are the ones with user visible messages (in the patch
3 of the first 4 hunks), the rest is just comments (and internal
documentation; for that see the doc/tm.texi changes).

2020-03-17  Jakub Jelinek  <jakub@redhat.com>

	* lra-spills.c (remove_pseudos): Fix up duplicated word issue in
	a dump message.
	* tree-sra.c (create_access_replacement): Fix up duplicated word issue
	in a comment.
	* read-rtl-function.c (find_param_by_name,
	function_reader::parse_enum_value, function_reader::get_insn_by_uid):
	Likewise.
	* spellcheck.c (get_edit_distance_cutoff): Likewise.
	* tree-data-ref.c (create_ifn_alias_checks): Likewise.
	* tree.def (SWITCH_EXPR): Likewise.
	* selftest.c (assert_str_contains): Likewise.
	* ipa-param-manipulation.h (class ipa_param_body_adjustments):
	Likewise.
	* tree-ssa-math-opts.c (convert_expand_mult_copysign): Likewise.
	* tree-ssa-loop-split.c (find_vdef_in_loop): Likewise.
	* langhooks.h (struct lang_hooks_for_decls): Likewise.
	* ipa-prop.h (struct ipa_param_descriptor): Likewise.
	* tree-ssa-strlen.c (handle_builtin_string_cmp, handle_store):
	Likewise.
	* tree-ssa-dom.c (simplify_stmt_for_jump_threading): Likewise.
	* tree-ssa-reassoc.c (reassociate_bb): Likewise.
	* tree.c (component_ref_size): Likewise.
	* hsa-common.c (hsa_init_compilation_unit_data): Likewise.
	* gimple-ssa-sprintf.c (get_string_length, format_string,
	format_directive): Likewise.
	* omp-grid.c (grid_process_kernel_body_copy): Likewise.
	* input.c (string_concat_db::get_string_concatenation,
	test_lexer_string_locations_ucn4): Likewise.
	* cfgexpand.c (pass_expand::execute): Likewise.
	* gimple-ssa-warn-restrict.c (builtin_memref::offset_out_of_bounds,
	maybe_diag_overlap): Likewise.
	* rtl.c (RTX_CODE_HWINT_P_1): Likewise.
	* shrink-wrap.c (spread_components): Likewise.
	* tree-ssa-dse.c (initialize_ao_ref_for_dse, valid_ao_ref_for_dse):
	Likewise.
	* tree-call-cdce.c (shrink_wrap_one_built_in_call_with_conds):
	Likewise.
	* dwarf2out.c (dwarf2out_early_finish): Likewise.
	* gimple-ssa-store-merging.c: Likewise.
	* ira-costs.c (record_operand_costs): Likewise.
	* tree-vect-loop.c (vectorizable_reduction): Likewise.
	* target.def (dispatch): Likewise.
	(validate_dims, gen_ccmp_first): Fix up duplicated word issue
	in documentation text.
	* doc/tm.texi: Regenerated.
	* config/i386/x86-tune.def (X86_TUNE_PARTIAL_FLAG_REG_STALL): Fix up
	duplicated word issue in a comment.
	* config/i386/i386.c (ix86_test_loading_unspec): Likewise.
	* config/i386/i386-features.c (remove_partial_avx_dependency):
	Likewise.
	* config/msp430/msp430.c (msp430_select_section): Likewise.
	* config/gcn/gcn-run.c (load_image): Likewise.
	* config/aarch64/aarch64-sve.md (sve_ld1r<mode>): Likewise.
	* config/aarch64/aarch64.c (aarch64_gen_adjusted_ldpstp): Likewise.
	* config/aarch64/falkor-tag-collision-avoidance.c
	(single_dest_per_chain): Likewise.
	* config/nvptx/nvptx.c (nvptx_record_fndecl): Likewise.
	* config/fr30/fr30.c (fr30_arg_partial_bytes): Likewise.
	* config/rs6000/rs6000-string.c (expand_cmp_vec_sequence): Likewise.
	* config/rs6000/rs6000-p8swap.c (replace_swapped_load_constant):
	Likewise.
	* config/rs6000/rs6000-c.c (rs6000_target_modify_macros): Likewise.
	* config/rs6000/rs6000.c (rs6000_option_override_internal): Likewise.
	* config/rs6000/rs6000-logue.c
	(rs6000_emit_probe_stack_range_stack_clash): Likewise.
	* config/nds32/nds32-md-auxiliary.c (nds32_split_ashiftdi3): Likewise.
	Fix various other issues in the comment.
c-family/
	* c-common.c (resolve_overloaded_builtin): Fix up duplicated word
	issue in a diagnostic message.
cp/
	* pt.c (tsubst): Fix up duplicated word issue in a diagnostic message.
	(lookup_template_class_1, tsubst_expr): Fix up duplicated word issue
	in a comment.
	* parser.c (cp_parser_statement, cp_parser_linkage_specification,
	cp_parser_placeholder_type_specifier,
	cp_parser_constraint_requires_parens): Likewise.
	* name-lookup.c (suggest_alternative_in_explicit_scope): Likewise.
fortran/
	* array.c (gfc_check_iter_variable): Fix up duplicated word issue
	in a comment.
	* arith.c (gfc_arith_concat): Likewise.
	* resolve.c (gfc_resolve_ref): Likewise.
	* frontend-passes.c (matmul_lhs_realloc): Likewise.
	* module.c (gfc_match_submodule, load_needed): Likewise.
	* trans-expr.c (gfc_init_se): Likewise.
2020-03-17 13:52:19 +01:00

2228 lines
61 KiB
C

/* read-rtl-function.c - Reader for RTL function dumps
Copyright (C) 2016-2020 Free Software Foundation, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
GCC is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "target.h"
#include "tree.h"
#include "diagnostic.h"
#include "read-md.h"
#include "rtl.h"
#include "cfghooks.h"
#include "stringpool.h"
#include "function.h"
#include "tree-cfg.h"
#include "cfg.h"
#include "basic-block.h"
#include "cfgrtl.h"
#include "memmodel.h"
#include "emit-rtl.h"
#include "cgraph.h"
#include "tree-pass.h"
#include "toplev.h"
#include "varasm.h"
#include "read-rtl-function.h"
#include "selftest.h"
#include "selftest-rtl.h"
#include "regs.h"
#include "function-abi.h"
/* Forward decls. */
class function_reader;
class fixup;
/* Edges are recorded when parsing the "insn-chain" directive,
and created at the end when all the blocks ought to exist.
This struct records an "edge-from" or "edge-to" directive seen
at LOC, which will be turned into an actual CFG edge once
the "insn-chain" is fully parsed. */
class deferred_edge
{
public:
deferred_edge (file_location loc, int src_bb_idx, int dest_bb_idx, int flags)
: m_loc (loc), m_src_bb_idx (src_bb_idx), m_dest_bb_idx (dest_bb_idx),
m_flags (flags)
{}
file_location m_loc;
int m_src_bb_idx;
int m_dest_bb_idx;
int m_flags;
};
/* Subclass of rtx_reader for reading function dumps. */
class function_reader : public rtx_reader
{
public:
function_reader ();
~function_reader ();
/* Overridden vfuncs of class md_reader. */
void handle_unknown_directive (file_location, const char *) FINAL OVERRIDE;
/* Overridden vfuncs of class rtx_reader. */
rtx read_rtx_operand (rtx x, int idx) FINAL OVERRIDE;
void handle_any_trailing_information (rtx x) FINAL OVERRIDE;
rtx postprocess (rtx) FINAL OVERRIDE;
const char *finalize_string (char *stringbuf) FINAL OVERRIDE;
rtx_insn **get_insn_by_uid (int uid);
tree parse_mem_expr (const char *desc);
private:
void parse_function ();
void create_function ();
void parse_param ();
void parse_insn_chain ();
void parse_block ();
int parse_bb_idx ();
void parse_edge (basic_block block, bool from);
rtx_insn *parse_insn (file_location loc, const char *name);
void parse_cfg (file_location loc);
void parse_crtl (file_location loc);
void create_edges ();
int parse_enum_value (int num_values, const char *const *strings);
void read_rtx_operand_u (rtx x, int idx);
void read_rtx_operand_i_or_n (rtx x, int idx, char format_char);
rtx read_rtx_operand_r (rtx x);
rtx extra_parsing_for_operand_code_0 (rtx x, int idx);
void add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx,
int insn_uid);
void add_fixup_note_insn_basic_block (file_location loc, rtx insn,
int operand_idx, int bb_idx);
void add_fixup_source_location (file_location loc, rtx_insn *insn,
const char *filename, int lineno, int colno);
void add_fixup_expr (file_location loc, rtx x,
const char *desc);
rtx consolidate_singletons (rtx x);
rtx parse_rtx ();
void maybe_read_location (rtx_insn *insn);
void handle_insn_uids ();
void apply_fixups ();
private:
struct uid_hash : int_hash <int, -1, -2> {};
hash_map<uid_hash, rtx_insn *> m_insns_by_uid;
auto_vec<fixup *> m_fixups;
rtx_insn *m_first_insn;
auto_vec<tree> m_fake_scope;
char *m_name;
bool m_have_crtl_directive;
basic_block m_bb_to_insert_after;
auto_vec <deferred_edge> m_deferred_edges;
int m_highest_bb_idx;
};
/* Abstract base class for recording post-processing steps that must be
done after reading a .rtl file. */
class fixup
{
public:
/* Constructor for a fixup at LOC affecting X. */
fixup (file_location loc, rtx x)
: m_loc (loc), m_rtx (x)
{}
virtual ~fixup () {}
virtual void apply (function_reader *reader) const = 0;
protected:
file_location m_loc;
rtx m_rtx;
};
/* An abstract subclass of fixup for post-processing steps that
act on a specific operand of a specific instruction. */
class operand_fixup : public fixup
{
public:
/* Constructor for a fixup at LOC affecting INSN's operand
with index OPERAND_IDX. */
operand_fixup (file_location loc, rtx insn, int operand_idx)
: fixup (loc, insn), m_operand_idx (operand_idx)
{}
protected:
int m_operand_idx;
};
/* A concrete subclass of operand_fixup: fixup an rtx_insn *
field based on an integer UID. */
class fixup_insn_uid : public operand_fixup
{
public:
/* Constructor for a fixup at LOC affecting INSN's operand
with index OPERAND_IDX. Record INSN_UID as the uid. */
fixup_insn_uid (file_location loc, rtx insn, int operand_idx, int insn_uid)
: operand_fixup (loc, insn, operand_idx),
m_insn_uid (insn_uid)
{}
void apply (function_reader *reader) const;
private:
int m_insn_uid;
};
/* A concrete subclass of operand_fixup: fix up a
NOTE_INSN_BASIC_BLOCK based on an integer block ID. */
class fixup_note_insn_basic_block : public operand_fixup
{
public:
fixup_note_insn_basic_block (file_location loc, rtx insn, int operand_idx,
int bb_idx)
: operand_fixup (loc, insn, operand_idx),
m_bb_idx (bb_idx)
{}
void apply (function_reader *reader) const;
private:
int m_bb_idx;
};
/* A concrete subclass of fixup (not operand_fixup): fix up
the expr of an rtx (REG or MEM) based on a textual dump. */
class fixup_expr : public fixup
{
public:
fixup_expr (file_location loc, rtx x, const char *desc)
: fixup (loc, x),
m_desc (xstrdup (desc))
{}
~fixup_expr () { free (m_desc); }
void apply (function_reader *reader) const;
private:
char *m_desc;
};
/* Return a textual description of the operand of INSN with
index OPERAND_IDX. */
static const char *
get_operand_name (rtx insn, int operand_idx)
{
gcc_assert (is_a <rtx_insn *> (insn));
switch (operand_idx)
{
case 0:
return "PREV_INSN";
case 1:
return "NEXT_INSN";
default:
return NULL;
}
}
/* Fixup an rtx_insn * field based on an integer UID, as read by READER. */
void
fixup_insn_uid::apply (function_reader *reader) const
{
rtx_insn **insn_from_uid = reader->get_insn_by_uid (m_insn_uid);
if (insn_from_uid)
XEXP (m_rtx, m_operand_idx) = *insn_from_uid;
else
{
const char *op_name = get_operand_name (m_rtx, m_operand_idx);
if (op_name)
error_at (m_loc,
"insn with UID %i not found for operand %i (`%s') of insn %i",
m_insn_uid, m_operand_idx, op_name, INSN_UID (m_rtx));
else
error_at (m_loc,
"insn with UID %i not found for operand %i of insn %i",
m_insn_uid, m_operand_idx, INSN_UID (m_rtx));
}
}
/* Fix up a NOTE_INSN_BASIC_BLOCK based on an integer block ID. */
void
fixup_note_insn_basic_block::apply (function_reader *) const
{
basic_block bb = BASIC_BLOCK_FOR_FN (cfun, m_bb_idx);
gcc_assert (bb);
NOTE_BASIC_BLOCK (m_rtx) = bb;
}
/* Fix up the expr of an rtx (REG or MEM) based on a textual dump
read by READER. */
void
fixup_expr::apply (function_reader *reader) const
{
tree expr = reader->parse_mem_expr (m_desc);
switch (GET_CODE (m_rtx))
{
case REG:
set_reg_attrs_for_decl_rtl (expr, m_rtx);
break;
case MEM:
set_mem_expr (m_rtx, expr);
break;
default:
gcc_unreachable ();
}
}
/* Strip trailing whitespace from DESC. */
static void
strip_trailing_whitespace (char *desc)
{
char *terminator = desc + strlen (desc);
while (desc < terminator)
{
terminator--;
if (ISSPACE (*terminator))
*terminator = '\0';
else
break;
}
}
/* Return the numeric value n for GET_NOTE_INSN_NAME (n) for STRING,
or fail if STRING isn't recognized. */
static int
parse_note_insn_name (const char *string)
{
for (int i = 0; i < NOTE_INSN_MAX; i++)
if (strcmp (string, GET_NOTE_INSN_NAME (i)) == 0)
return i;
fatal_with_file_and_line ("unrecognized NOTE_INSN name: `%s'", string);
}
/* Return the register number for NAME, or return -1 if it isn't
recognized. */
static int
lookup_reg_by_dump_name (const char *name)
{
for (int i = 0; i < FIRST_PSEUDO_REGISTER; i++)
if (reg_names[i][0]
&& ! strcmp (name, reg_names[i]))
return i;
/* Also lookup virtuals. */
if (!strcmp (name, "virtual-incoming-args"))
return VIRTUAL_INCOMING_ARGS_REGNUM;
if (!strcmp (name, "virtual-stack-vars"))
return VIRTUAL_STACK_VARS_REGNUM;
if (!strcmp (name, "virtual-stack-dynamic"))
return VIRTUAL_STACK_DYNAMIC_REGNUM;
if (!strcmp (name, "virtual-outgoing-args"))
return VIRTUAL_OUTGOING_ARGS_REGNUM;
if (!strcmp (name, "virtual-cfa"))
return VIRTUAL_CFA_REGNUM;
if (!strcmp (name, "virtual-preferred-stack-boundary"))
return VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM;
/* TODO: handle "virtual-reg-%d". */
/* In compact mode, pseudos are printed with '< and '>' wrapping the regno,
offseting it by (LAST_VIRTUAL_REGISTER + 1), so that the
first non-virtual pseudo is dumped as "<0>". */
if (name[0] == '<' && name[strlen (name) - 1] == '>')
{
int dump_num = atoi (name + 1);
return dump_num + LAST_VIRTUAL_REGISTER + 1;
}
/* Not found. */
return -1;
}
/* class function_reader : public rtx_reader */
/* function_reader's constructor. */
function_reader::function_reader ()
: rtx_reader (true),
m_first_insn (NULL),
m_name (NULL),
m_have_crtl_directive (false),
m_bb_to_insert_after (NULL),
m_highest_bb_idx (EXIT_BLOCK)
{
}
/* function_reader's destructor. */
function_reader::~function_reader ()
{
int i;
fixup *f;
FOR_EACH_VEC_ELT (m_fixups, i, f)
delete f;
free (m_name);
}
/* Implementation of rtx_reader::handle_unknown_directive,
for parsing the remainder of a directive with name NAME
seen at START_LOC.
Require a top-level "function" directive, as emitted by
print_rtx_function, and parse it. */
void
function_reader::handle_unknown_directive (file_location start_loc,
const char *name)
{
if (strcmp (name, "function"))
fatal_at (start_loc, "expected 'function'");
if (flag_lto)
error ("%<__RTL%> function cannot be compiled with %<-flto%>");
parse_function ();
}
/* Parse the output of print_rtx_function (or hand-written data in the
same format), having already parsed the "(function" heading, and
finishing immediately before the final ")".
The "param" and "crtl" clauses are optional. */
void
function_reader::parse_function ()
{
m_name = xstrdup (read_string (0));
create_function ();
while (1)
{
int c = read_skip_spaces ();
if (c == ')')
{
unread_char (c);
break;
}
unread_char (c);
require_char ('(');
file_location loc = get_current_location ();
struct md_name directive;
read_name (&directive);
if (strcmp (directive.string, "param") == 0)
parse_param ();
else if (strcmp (directive.string, "insn-chain") == 0)
parse_insn_chain ();
else if (strcmp (directive.string, "crtl") == 0)
parse_crtl (loc);
else
fatal_with_file_and_line ("unrecognized directive: %s",
directive.string);
}
handle_insn_uids ();
apply_fixups ();
/* Rebuild the JUMP_LABEL field of any JUMP_INSNs in the chain, and the
LABEL_NUSES of any CODE_LABELs.
This has to happen after apply_fixups, since only after then do
LABEL_REFs have their label_ref_label set up. */
rebuild_jump_labels (get_insns ());
crtl->init_stack_alignment ();
}
/* Set up state for the function *before* fixups are applied.
Create "cfun" and a decl for the function.
By default, every function decl is hardcoded as
int test_1 (int i, int j, int k);
Set up various other state:
- the cfg and basic blocks (edges are created later, *after* fixups
are applied).
- add the function to the callgraph. */
void
function_reader::create_function ()
{
/* We start in cfgrtl mode, rather than cfglayout mode. */
rtl_register_cfg_hooks ();
/* When run from selftests or "rtl1", cfun is NULL.
When run from "cc1" for a C function tagged with __RTL, cfun is the
tagged function. */
if (!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 (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;
}
gcc_assert (cfun);
gcc_assert (current_function_decl);
tree fndecl = current_function_decl;
/* Mark this function as being specified as __RTL. */
cfun->curr_properties |= PROP_rtl;
/* cc1 normally inits DECL_INITIAL (fndecl) to be error_mark_node.
Create a dummy block for it. */
DECL_INITIAL (fndecl) = make_node (BLOCK);
cfun->curr_properties = (PROP_cfg | PROP_rtl);
/* Do we need this to force cgraphunit.c to output the function? */
DECL_EXTERNAL (fndecl) = 0;
DECL_PRESERVE_P (fndecl) = 1;
/* Add to cgraph. */
cgraph_node::finalize_function (fndecl, false);
/* Create bare-bones cfg. This creates the entry and exit blocks. */
init_empty_tree_cfg_for_function (cfun);
ENTRY_BLOCK_PTR_FOR_FN (cfun)->flags |= BB_RTL;
EXIT_BLOCK_PTR_FOR_FN (cfun)->flags |= BB_RTL;
init_rtl_bb_info (ENTRY_BLOCK_PTR_FOR_FN (cfun));
init_rtl_bb_info (EXIT_BLOCK_PTR_FOR_FN (cfun));
m_bb_to_insert_after = ENTRY_BLOCK_PTR_FOR_FN (cfun);
}
/* Look within the params of FNDECL for a param named NAME.
Return NULL_TREE if one isn't found. */
static tree
find_param_by_name (tree fndecl, const char *name)
{
for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = TREE_CHAIN (arg))
if (id_equal (DECL_NAME (arg), name))
return arg;
return NULL_TREE;
}
/* Parse the content of a "param" directive, having already parsed the
"(param". Consume the trailing ')'. */
void
function_reader::parse_param ()
{
require_char_ws ('"');
file_location loc = get_current_location ();
char *name = read_quoted_string ();
/* Lookup param by name. */
tree t_param = find_param_by_name (cfun->decl, name);
if (!t_param)
fatal_at (loc, "param not found: %s", name);
/* Parse DECL_RTL. */
require_char_ws ('(');
require_word_ws ("DECL_RTL");
DECL_WRTL_CHECK (t_param)->decl_with_rtl.rtl = parse_rtx ();
require_char_ws (')');
/* Parse DECL_RTL_INCOMING. */
require_char_ws ('(');
require_word_ws ("DECL_RTL_INCOMING");
DECL_INCOMING_RTL (t_param) = parse_rtx ();
require_char_ws (')');
require_char_ws (')');
}
/* Parse zero or more child insn elements within an
"insn-chain" element. Consume the trailing ')'. */
void
function_reader::parse_insn_chain ()
{
while (1)
{
int c = read_skip_spaces ();
file_location loc = get_current_location ();
if (c == ')')
break;
else if (c == '(')
{
struct md_name directive;
read_name (&directive);
if (strcmp (directive.string, "block") == 0)
parse_block ();
else
parse_insn (loc, directive.string);
}
else
fatal_at (loc, "expected '(' or ')'");
}
create_edges ();
}
/* Parse zero or more child directives (edges and insns) within a
"block" directive, having already parsed the "(block " heading.
Consume the trailing ')'. */
void
function_reader::parse_block ()
{
/* Parse the index value from the dump. This will be an integer;
we don't support "entry" or "exit" here (unlike for edges). */
struct md_name name;
read_name (&name);
int bb_idx = atoi (name.string);
/* The term "index" has two meanings for basic blocks in a CFG:
(a) the "index" field within struct basic_block_def.
(b) the index of a basic_block within the cfg's x_basic_block_info
vector, as accessed via BASIC_BLOCK_FOR_FN.
These can get out-of-sync when basic blocks are optimized away.
They get back in sync by "compact_blocks".
We reconstruct cfun->cfg->x_basic_block_info->m_vecdata with NULL
values in it for any missing basic blocks, so that (a) == (b) for
all of the blocks we create. The doubly-linked list of basic
blocks (next_bb/prev_bb) skips over these "holes". */
if (m_highest_bb_idx < bb_idx)
m_highest_bb_idx = bb_idx;
size_t new_size = m_highest_bb_idx + 1;
if (basic_block_info_for_fn (cfun)->length () < new_size)
vec_safe_grow_cleared (basic_block_info_for_fn (cfun), new_size);
last_basic_block_for_fn (cfun) = new_size;
/* Create the basic block.
We can't call create_basic_block and use the regular RTL block-creation
hooks, since this creates NOTE_INSN_BASIC_BLOCK instances. We don't
want to do that; we want to use the notes we were provided with. */
basic_block bb = alloc_block ();
init_rtl_bb_info (bb);
bb->index = bb_idx;
bb->flags = BB_NEW | BB_RTL;
link_block (bb, m_bb_to_insert_after);
m_bb_to_insert_after = bb;
n_basic_blocks_for_fn (cfun)++;
SET_BASIC_BLOCK_FOR_FN (cfun, bb_idx, bb);
BB_SET_PARTITION (bb, BB_UNPARTITIONED);
/* Handle insns, edge-from and edge-to directives. */
while (1)
{
int c = read_skip_spaces ();
file_location loc = get_current_location ();
if (c == ')')
break;
else if (c == '(')
{
struct md_name directive;
read_name (&directive);
if (strcmp (directive.string, "edge-from") == 0)
parse_edge (bb, true);
else if (strcmp (directive.string, "edge-to") == 0)
parse_edge (bb, false);
else
{
rtx_insn *insn = parse_insn (loc, directive.string);
set_block_for_insn (insn, bb);
if (!BB_HEAD (bb))
BB_HEAD (bb) = insn;
BB_END (bb) = insn;
}
}
else
fatal_at (loc, "expected '(' or ')'");
}
}
/* Subroutine of function_reader::parse_edge.
Parse a basic block index, handling "entry" and "exit". */
int
function_reader::parse_bb_idx ()
{
struct md_name name;
read_name (&name);
if (strcmp (name.string, "entry") == 0)
return ENTRY_BLOCK;
if (strcmp (name.string, "exit") == 0)
return EXIT_BLOCK;
return atoi (name.string);
}
/* Subroutine of parse_edge_flags.
Parse TOK, a token such as "FALLTHRU", converting to the flag value.
Issue an error if the token is unrecognized. */
static int
parse_edge_flag_token (const char *tok)
{
#define DEF_EDGE_FLAG(NAME,IDX) \
do { \
if (strcmp (tok, #NAME) == 0) \
return EDGE_##NAME; \
} while (0);
#include "cfg-flags.def"
#undef DEF_EDGE_FLAG
error ("unrecognized edge flag: %qs", tok);
return 0;
}
/* Subroutine of function_reader::parse_edge.
Parse STR and convert to a flag value (or issue an error).
The parser uses strtok and hence modifiers STR in-place. */
static int
parse_edge_flags (char *str)
{
int result = 0;
char *tok = strtok (str, "| ");
while (tok)
{
result |= parse_edge_flag_token (tok);
tok = strtok (NULL, "| ");
}
return result;
}
/* Parse an "edge-from" or "edge-to" directive within the "block"
directive for BLOCK, having already parsed the "(edge" heading.
Consume the final ")". Record the edge within m_deferred_edges.
FROM is true for an "edge-from" directive, false for an "edge-to"
directive. */
void
function_reader::parse_edge (basic_block block, bool from)
{
gcc_assert (block);
int this_bb_idx = block->index;
file_location loc = get_current_location ();
int other_bb_idx = parse_bb_idx ();
/* "(edge-from 2)" means src = 2, dest = this_bb_idx, whereas
"(edge-to 3)" means src = this_bb_idx, dest = 3. */
int src_idx = from ? other_bb_idx : this_bb_idx;
int dest_idx = from ? this_bb_idx : other_bb_idx;
/* Optional "(flags)". */
int flags = 0;
int c = read_skip_spaces ();
if (c == '(')
{
require_word_ws ("flags");
require_char_ws ('"');
char *str = read_quoted_string ();
flags = parse_edge_flags (str);
require_char_ws (')');
}
else
unread_char (c);
require_char_ws (')');
/* This BB already exists, but the other BB might not yet.
For now, save the edges, and create them at the end of insn-chain
processing. */
/* For now, only process the (edge-from) to this BB, and (edge-to)
that go to the exit block.
FIXME: we don't yet verify that the edge-from and edge-to directives
are consistent. */
if (from || dest_idx == EXIT_BLOCK)
m_deferred_edges.safe_push (deferred_edge (loc, src_idx, dest_idx, flags));
}
/* Parse an rtx instruction, having parsed the opening and parenthesis, and
name NAME, seen at START_LOC, by calling read_rtx_code, calling
set_first_insn and set_last_insn as appropriate, and
adding the insn to the insn chain.
Consume the trailing ')'. */
rtx_insn *
function_reader::parse_insn (file_location start_loc, const char *name)
{
rtx x = read_rtx_code (name);
if (!x)
fatal_at (start_loc, "expected insn type; got '%s'", name);
rtx_insn *insn = dyn_cast <rtx_insn *> (x);
if (!insn)
fatal_at (start_loc, "expected insn type; got '%s'", name);
/* Consume the trailing ')'. */
require_char_ws (')');
rtx_insn *last_insn = get_last_insn ();
/* Add "insn" to the insn chain. */
if (last_insn)
{
gcc_assert (NEXT_INSN (last_insn) == NULL);
SET_NEXT_INSN (last_insn) = insn;
}
SET_PREV_INSN (insn) = last_insn;
/* Add it to the sequence. */
set_last_insn (insn);
if (!m_first_insn)
{
m_first_insn = insn;
set_first_insn (insn);
}
if (rtx_code_label *label = dyn_cast <rtx_code_label *> (insn))
maybe_set_max_label_num (label);
return insn;
}
/* Postprocessing subroutine for parse_insn_chain: all the basic blocks
should have been created by now; create the edges that were seen. */
void
function_reader::create_edges ()
{
int i;
deferred_edge *de;
FOR_EACH_VEC_ELT (m_deferred_edges, i, de)
{
/* The BBs should already have been created by parse_block. */
basic_block src = BASIC_BLOCK_FOR_FN (cfun, de->m_src_bb_idx);
if (!src)
fatal_at (de->m_loc, "error: block index %i not found",
de->m_src_bb_idx);
basic_block dst = BASIC_BLOCK_FOR_FN (cfun, de->m_dest_bb_idx);
if (!dst)
fatal_at (de->m_loc, "error: block with index %i not found",
de->m_dest_bb_idx);
unchecked_make_edge (src, dst, de->m_flags);
}
}
/* Parse a "crtl" directive, having already parsed the "(crtl" heading
at location LOC.
Consume the final ")". */
void
function_reader::parse_crtl (file_location loc)
{
if (m_have_crtl_directive)
error_at (loc, "more than one 'crtl' directive");
m_have_crtl_directive = true;
/* return_rtx. */
require_char_ws ('(');
require_word_ws ("return_rtx");
crtl->return_rtx = parse_rtx ();
require_char_ws (')');
require_char_ws (')');
}
/* Parse operand IDX of X, returning X, or an equivalent rtx
expression (for consolidating singletons).
This is an overridden implementation of rtx_reader::read_rtx_operand for
function_reader, handling various extra data printed by print_rtx,
and sometimes calling the base class implementation. */
rtx
function_reader::read_rtx_operand (rtx x, int idx)
{
RTX_CODE code = GET_CODE (x);
const char *format_ptr = GET_RTX_FORMAT (code);
const char format_char = format_ptr[idx];
struct md_name name;
/* Override the regular parser for some format codes. */
switch (format_char)
{
case 'e':
if (idx == 7 && CALL_P (x))
{
m_in_call_function_usage = true;
return rtx_reader::read_rtx_operand (x, idx);
m_in_call_function_usage = false;
}
else
return rtx_reader::read_rtx_operand (x, idx);
break;
case 'u':
read_rtx_operand_u (x, idx);
/* Don't run regular parser for 'u'. */
return x;
case 'i':
case 'n':
read_rtx_operand_i_or_n (x, idx, format_char);
/* Don't run regular parser for these codes. */
return x;
case 'B':
gcc_assert (is_compact ());
/* Compact mode doesn't store BBs. */
/* Don't run regular parser. */
return x;
case 'r':
/* Don't run regular parser for 'r'. */
return read_rtx_operand_r (x);
default:
break;
}
/* Call base class implementation. */
x = rtx_reader::read_rtx_operand (x, idx);
/* Handle any additional parsing needed to handle what the dump
could contain. */
switch (format_char)
{
case '0':
x = extra_parsing_for_operand_code_0 (x, idx);
break;
case 'w':
if (!is_compact ())
{
/* Strip away the redundant hex dump of the value. */
require_char_ws ('[');
read_name (&name);
require_char_ws (']');
}
break;
default:
break;
}
return x;
}
/* Parse operand IDX of X, of code 'u', when reading function dumps.
The RTL file recorded the ID of an insn (or 0 for NULL); we
must store this as a pointer, but the insn might not have
been loaded yet. Store the ID away for now, via a fixup. */
void
function_reader::read_rtx_operand_u (rtx x, int idx)
{
/* In compact mode, the PREV/NEXT insn uids are not dumped, so skip
the "uu" when reading. */
if (is_compact () && GET_CODE (x) != LABEL_REF)
return;
struct md_name name;
file_location loc = read_name (&name);
int insn_id = atoi (name.string);
if (insn_id)
add_fixup_insn_uid (loc, x, idx, insn_id);
}
/* Read a name, looking for a match against a string found in array
STRINGS of size NUM_VALUES.
Return the index of the matched string, or emit an error. */
int
function_reader::parse_enum_value (int num_values, const char *const *strings)
{
struct md_name name;
read_name (&name);
for (int i = 0; i < num_values; i++)
{
if (strcmp (name.string, strings[i]) == 0)
return i;
}
error ("unrecognized enum value: %qs", name.string);
return 0;
}
/* Parse operand IDX of X, of code 'i' or 'n' (as specified by FORMAT_CHAR).
Special-cased handling of these, for reading function dumps. */
void
function_reader::read_rtx_operand_i_or_n (rtx x, int idx,
char format_char)
{
/* Handle some of the extra information that print_rtx
can write out for these cases. */
/* print_rtx only writes out operand 5 for notes
for NOTE_KIND values NOTE_INSN_DELETED_LABEL
and NOTE_INSN_DELETED_DEBUG_LABEL. */
if (idx == 5 && NOTE_P (x))
return;
if (idx == 4 && INSN_P (x))
{
maybe_read_location (as_a <rtx_insn *> (x));
return;
}
/* INSN_CODEs aren't printed in compact mode, so don't attempt to
parse them. */
if (is_compact ()
&& INSN_P (x)
&& &INSN_CODE (x) == &XINT (x, idx))
{
INSN_CODE (x) = -1;
return;
}
/* Handle UNSPEC and UNSPEC_VOLATILE's operand 1. */
#if !defined(GENERATOR_FILE) && NUM_UNSPECV_VALUES > 0
if (idx == 1
&& GET_CODE (x) == UNSPEC_VOLATILE)
{
XINT (x, 1)
= parse_enum_value (NUM_UNSPECV_VALUES, unspecv_strings);
return;
}
#endif
#if !defined(GENERATOR_FILE) && NUM_UNSPEC_VALUES > 0
if (idx == 1
&& (GET_CODE (x) == UNSPEC
|| GET_CODE (x) == UNSPEC_VOLATILE))
{
XINT (x, 1)
= parse_enum_value (NUM_UNSPEC_VALUES, unspec_strings);
return;
}
#endif
struct md_name name;
read_name (&name);
int value;
if (format_char == 'n')
value = parse_note_insn_name (name.string);
else
value = atoi (name.string);
XINT (x, idx) = value;
}
/* Parse the 'r' operand of X, returning X, or an equivalent rtx
expression (for consolidating singletons).
Special-cased handling of code 'r' for reading function dumps. */
rtx
function_reader::read_rtx_operand_r (rtx x)
{
struct md_name name;
file_location loc = read_name (&name);
int regno = lookup_reg_by_dump_name (name.string);
if (regno == -1)
fatal_at (loc, "unrecognized register: '%s'", name.string);
set_regno_raw (x, regno, 1);
/* Consolidate singletons. */
x = consolidate_singletons (x);
ORIGINAL_REGNO (x) = regno;
/* Parse extra stuff at end of 'r'.
We may have zero, one, or two sections marked by square
brackets. */
int ch = read_skip_spaces ();
bool expect_original_regno = false;
if (ch == '[')
{
file_location loc = get_current_location ();
char *desc = read_until ("]", true);
strip_trailing_whitespace (desc);
const char *desc_start = desc;
/* If ORIGINAL_REGNO (rtx) != regno, we will have:
"orig:%i", ORIGINAL_REGNO (rtx).
Consume it, we don't set ORIGINAL_REGNO, since we can
get that from the 2nd copy later. */
if (strncmp (desc, "orig:", 5) == 0)
{
expect_original_regno = true;
desc_start += 5;
/* Skip to any whitespace following the integer. */
const char *space = strchr (desc_start, ' ');
if (space)
desc_start = space + 1;
}
/* Any remaining text may be the REG_EXPR. Alternatively we have
no REG_ATTRS, and instead we have ORIGINAL_REGNO. */
if (ISDIGIT (*desc_start))
{
/* Assume we have ORIGINAL_REGNO. */
ORIGINAL_REGNO (x) = atoi (desc_start);
}
else
{
/* Assume we have REG_EXPR. */
add_fixup_expr (loc, x, desc_start);
}
free (desc);
}
else
unread_char (ch);
if (expect_original_regno)
{
require_char_ws ('[');
char *desc = read_until ("]", true);
ORIGINAL_REGNO (x) = atoi (desc);
free (desc);
}
return x;
}
/* Additional parsing for format code '0' in dumps, handling a variety
of special-cases in print_rtx, when parsing operand IDX of X.
Return X, or possibly a reallocated copy of X. */
rtx
function_reader::extra_parsing_for_operand_code_0 (rtx x, int idx)
{
RTX_CODE code = GET_CODE (x);
int c;
struct md_name name;
if (idx == 1 && code == SYMBOL_REF)
{
/* Possibly wrote " [flags %#x]", SYMBOL_REF_FLAGS (in_rtx). */
c = read_skip_spaces ();
if (c == '[')
{
file_location loc = read_name (&name);
if (strcmp (name.string, "flags"))
error_at (loc, "was expecting `%s'", "flags");
read_name (&name);
SYMBOL_REF_FLAGS (x) = strtol (name.string, NULL, 16);
/* The standard RTX_CODE_SIZE (SYMBOL_REF) used when allocating
x doesn't have space for the block_symbol information, so
we must reallocate it if this flag is set. */
if (SYMBOL_REF_HAS_BLOCK_INFO_P (x))
{
/* Emulate the allocation normally done by
varasm.c:create_block_symbol. */
unsigned int size = RTX_HDR_SIZE + sizeof (struct block_symbol);
rtx new_x = (rtx) ggc_internal_alloc (size);
/* Copy data over from the smaller SYMBOL_REF. */
memcpy (new_x, x, RTX_CODE_SIZE (SYMBOL_REF));
x = new_x;
/* We can't reconstruct SYMBOL_REF_BLOCK; set it to NULL. */
SYMBOL_REF_BLOCK (x) = NULL;
/* Zero the offset. */
SYMBOL_REF_BLOCK_OFFSET (x) = 0;
}
require_char (']');
}
else
unread_char (c);
/* If X had a non-NULL SYMBOL_REF_DECL,
rtx_writer::print_rtx_operand_code_0 would have dumped it
using print_node_brief.
Skip the content for now. */
c = read_skip_spaces ();
if (c == '<')
{
while (1)
{
char ch = read_char ();
if (ch == '>')
break;
}
}
else
unread_char (c);
}
else if (idx == 3 && code == NOTE)
{
/* Note-specific data appears for operand 3, which annoyingly
is before the enum specifying which kind of note we have
(operand 4). */
c = read_skip_spaces ();
if (c == '[')
{
/* Possibly data for a NOTE_INSN_BASIC_BLOCK, of the form:
[bb %d]. */
file_location bb_loc = read_name (&name);
if (strcmp (name.string, "bb"))
error_at (bb_loc, "was expecting `%s'", "bb");
read_name (&name);
int bb_idx = atoi (name.string);
add_fixup_note_insn_basic_block (bb_loc, x, idx,
bb_idx);
require_char_ws (']');
}
else
unread_char (c);
}
return x;
}
/* Implementation of rtx_reader::handle_any_trailing_information.
Handle the various additional information that print-rtl.c can
write after the regular fields, when parsing X. */
void
function_reader::handle_any_trailing_information (rtx x)
{
struct md_name name;
switch (GET_CODE (x))
{
case MEM:
{
int ch;
require_char_ws ('[');
read_name (&name);
set_mem_alias_set (x, atoi (name.string));
/* We have either a MEM_EXPR, or a space. */
if (peek_char () != ' ')
{
file_location loc = get_current_location ();
char *desc = read_until (" +", false);
add_fixup_expr (loc, consolidate_singletons (x), desc);
free (desc);
}
else
read_char ();
/* We may optionally have '+' for MEM_OFFSET_KNOWN_P. */
ch = read_skip_spaces ();
if (ch == '+')
{
read_name (&name);
set_mem_offset (x, atoi (name.string));
}
else
unread_char (ch);
/* Handle optional " S" for MEM_SIZE. */
ch = read_skip_spaces ();
if (ch == 'S')
{
read_name (&name);
set_mem_size (x, atoi (name.string));
}
else
unread_char (ch);
/* Handle optional " A" for MEM_ALIGN. */
ch = read_skip_spaces ();
if (ch == 'A' && peek_char () != 'S')
{
read_name (&name);
set_mem_align (x, atoi (name.string));
}
else
unread_char (ch);
/* Handle optional " AS" for MEM_ADDR_SPACE. */
ch = read_skip_spaces ();
if (ch == 'A' && peek_char () == 'S')
{
read_char ();
read_name (&name);
set_mem_addr_space (x, atoi (name.string));
}
else
unread_char (ch);
require_char (']');
}
break;
case CODE_LABEL:
/* Assume that LABEL_NUSES was not dumped. */
/* TODO: parse LABEL_KIND. */
/* For now, skip until closing ')'. */
do
{
char ch = read_char ();
if (ch == ')')
{
unread_char (ch);
break;
}
}
while (1);
break;
default:
break;
}
}
/* Parse a tree dump for a MEM_EXPR in DESC and turn it back into a tree.
We handle "<retval>" and param names within cfun, but for anything else
we "cheat" by building a global VAR_DECL of type "int" with that name
(returning the same global for a name if we see the same name more
than once). */
tree
function_reader::parse_mem_expr (const char *desc)
{
tree fndecl = cfun->decl;
if (strcmp (desc, "<retval>") == 0)
return DECL_RESULT (fndecl);
tree param = find_param_by_name (fndecl, desc);
if (param)
return param;
/* Search within decls we already created.
FIXME: use a hash rather than linear search. */
int i;
tree t;
FOR_EACH_VEC_ELT (m_fake_scope, i, t)
if (id_equal (DECL_NAME (t), desc))
return t;
/* Not found? Create it.
This allows mimicking of real data but avoids having to specify
e.g. names of locals, params etc.
Though this way we don't know if we have a PARM_DECL vs a VAR_DECL,
and we don't know the types. Fake it by making everything be
a VAR_DECL of "int" type. */
t = build_decl (UNKNOWN_LOCATION, VAR_DECL,
get_identifier (desc),
integer_type_node);
m_fake_scope.safe_push (t);
return t;
}
/* Record that at LOC we saw an insn uid INSN_UID for the operand with index
OPERAND_IDX within INSN, so that the pointer value can be fixed up in
later post-processing. */
void
function_reader::add_fixup_insn_uid (file_location loc, rtx insn, int operand_idx,
int insn_uid)
{
m_fixups.safe_push (new fixup_insn_uid (loc, insn, operand_idx, insn_uid));
}
/* Record that at LOC we saw an basic block index BB_IDX for the operand with index
OPERAND_IDX within INSN, so that the pointer value can be fixed up in
later post-processing. */
void
function_reader::add_fixup_note_insn_basic_block (file_location loc, rtx insn,
int operand_idx, int bb_idx)
{
m_fixups.safe_push (new fixup_note_insn_basic_block (loc, insn, operand_idx,
bb_idx));
}
/* Placeholder hook for recording source location information seen in a dump.
This is empty for now. */
void
function_reader::add_fixup_source_location (file_location, rtx_insn *,
const char *, int, int)
{
}
/* Record that at LOC we saw textual description DESC of the MEM_EXPR or REG_EXPR
of INSN, so that the fields can be fixed up in later post-processing. */
void
function_reader::add_fixup_expr (file_location loc, rtx insn,
const char *desc)
{
gcc_assert (desc);
/* Fail early if the RTL reader erroneously hands us an int. */
gcc_assert (!ISDIGIT (desc[0]));
m_fixups.safe_push (new fixup_expr (loc, insn, desc));
}
/* Helper function for consolidate_reg. Return the global rtx for
the register with regno REGNO. */
static rtx
lookup_global_register (int regno)
{
/* We can't use a switch here, as some of the REGNUMs might not be constants
for some targets. */
if (regno == STACK_POINTER_REGNUM)
return stack_pointer_rtx;
else if (regno == FRAME_POINTER_REGNUM)
return frame_pointer_rtx;
else if (regno == HARD_FRAME_POINTER_REGNUM)
return hard_frame_pointer_rtx;
else if (regno == ARG_POINTER_REGNUM)
return arg_pointer_rtx;
else if (regno == VIRTUAL_INCOMING_ARGS_REGNUM)
return virtual_incoming_args_rtx;
else if (regno == VIRTUAL_STACK_VARS_REGNUM)
return virtual_stack_vars_rtx;
else if (regno == VIRTUAL_STACK_DYNAMIC_REGNUM)
return virtual_stack_dynamic_rtx;
else if (regno == VIRTUAL_OUTGOING_ARGS_REGNUM)
return virtual_outgoing_args_rtx;
else if (regno == VIRTUAL_CFA_REGNUM)
return virtual_cfa_rtx;
else if (regno == VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM)
return virtual_preferred_stack_boundary_rtx;
#ifdef return_ADDRESS_POINTER_REGNUM
else if (regno == RETURN_ADDRESS_POINTER_REGNUM)
return return_address_pointer_rtx;
#endif
return NULL;
}
/* Ensure that the backend can cope with a REG with regno REGNO.
Normally REG instances are created by gen_reg_rtx which updates
regno_reg_rtx, growing it as necessary.
The REG instances created from the dumpfile weren't created this
way, so we need to manually update regno_reg_rtx. */
static void
ensure_regno (int regno)
{
if (reg_rtx_no < regno + 1)
reg_rtx_no = regno + 1;
crtl->emit.ensure_regno_capacity ();
gcc_assert (regno < crtl->emit.regno_pointer_align_length);
}
/* Helper function for consolidate_singletons, for handling REG instances.
Given REG instance X of some regno, return the singleton rtx for that
regno, if it exists, or X. */
static rtx
consolidate_reg (rtx x)
{
gcc_assert (GET_CODE (x) == REG);
unsigned int regno = REGNO (x);
ensure_regno (regno);
/* Some register numbers have their rtx created in init_emit_regs
e.g. stack_pointer_rtx for STACK_POINTER_REGNUM.
Consolidate on this. */
rtx global_reg = lookup_global_register (regno);
if (global_reg)
return global_reg;
/* Populate regno_reg_rtx if necessary. */
if (regno_reg_rtx[regno] == NULL)
regno_reg_rtx[regno] = x;
/* Use it. */
gcc_assert (GET_CODE (regno_reg_rtx[regno]) == REG);
gcc_assert (REGNO (regno_reg_rtx[regno]) == regno);
if (GET_MODE (x) == GET_MODE (regno_reg_rtx[regno]))
return regno_reg_rtx[regno];
return x;
}
/* When reading RTL function dumps, we must consolidate some
rtx so that we use singletons where singletons are expected
(e.g. we don't want multiple "(const_int 0 [0])" rtx, since
these are tested via pointer equality against const0_rtx.
Return the equivalent singleton rtx for X, if any, otherwise X. */
rtx
function_reader::consolidate_singletons (rtx x)
{
if (!x)
return x;
switch (GET_CODE (x))
{
case PC: return pc_rtx;
case RETURN: return ret_rtx;
case SIMPLE_RETURN: return simple_return_rtx;
case CC0: return cc0_rtx;
case REG:
return consolidate_reg (x);
case CONST_INT:
return gen_rtx_CONST_INT (GET_MODE (x), INTVAL (x));
default:
break;
}
return x;
}
/* Parse an rtx directive, including both the opening/closing parentheses,
and the name. */
rtx
function_reader::parse_rtx ()
{
require_char_ws ('(');
struct md_name directive;
read_name (&directive);
rtx result
= consolidate_singletons (read_rtx_code (directive.string));
require_char_ws (')');
return result;
}
/* Implementation of rtx_reader::postprocess for reading function dumps.
Return the equivalent singleton rtx for X, if any, otherwise X. */
rtx
function_reader::postprocess (rtx x)
{
return consolidate_singletons (x);
}
/* Implementation of rtx_reader::finalize_string for reading function dumps.
Make a GC-managed copy of STRINGBUF. */
const char *
function_reader::finalize_string (char *stringbuf)
{
return ggc_strdup (stringbuf);
}
/* Attempt to parse optional location information for insn INSN, as
potentially written out by rtx_writer::print_rtx_operand_code_i.
We look for a quoted string followed by a colon. */
void
function_reader::maybe_read_location (rtx_insn *insn)
{
file_location loc = get_current_location ();
/* Attempt to parse a quoted string. */
int ch = read_skip_spaces ();
if (ch == '"')
{
char *filename = read_quoted_string ();
require_char (':');
struct md_name line_num;
read_name (&line_num);
int column = 0;
int ch = read_char ();
if (ch == ':')
{
struct md_name column_num;
read_name (&column_num);
column = atoi (column_num.string);
}
else
unread_char (ch);
add_fixup_source_location (loc, insn, filename,
atoi (line_num.string),
column);
}
else
unread_char (ch);
}
/* Postprocessing subroutine of function_reader::parse_function.
Populate m_insns_by_uid. */
void
function_reader::handle_insn_uids ()
{
/* Locate the currently assigned INSN_UID values, storing
them in m_insns_by_uid. */
int max_uid = 0;
for (rtx_insn *insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
if (m_insns_by_uid.get (INSN_UID (insn)))
error ("duplicate insn UID: %i", INSN_UID (insn));
m_insns_by_uid.put (INSN_UID (insn), insn);
if (INSN_UID (insn) > max_uid)
max_uid = INSN_UID (insn);
}
/* Ensure x_cur_insn_uid is 1 more than the biggest insn UID seen.
This is normally updated by the various make_*insn_raw functions. */
crtl->emit.x_cur_insn_uid = max_uid + 1;
}
/* Apply all of the recorded fixups. */
void
function_reader::apply_fixups ()
{
int i;
fixup *f;
FOR_EACH_VEC_ELT (m_fixups, i, f)
f->apply (this);
}
/* Given a UID value, try to locate a pointer to the corresponding
rtx_insn *, or NULL if it can't be found. */
rtx_insn **
function_reader::get_insn_by_uid (int uid)
{
return m_insns_by_uid.get (uid);
}
/* Run the RTL dump parser, parsing a dump located at PATH.
Return true iff the file was successfully parsed. */
bool
read_rtl_function_body (const char *path)
{
initialize_rtl ();
crtl->abi = &default_function_abi;
init_emit ();
init_varasm_status ();
function_reader reader;
if (!reader.read_file (path))
return false;
return true;
}
/* Run the RTL dump parser on the range of lines between START_LOC and
END_LOC (including those lines). */
bool
read_rtl_function_body_from_file_range (location_t start_loc,
location_t end_loc)
{
expanded_location exploc_start = expand_location (start_loc);
expanded_location exploc_end = expand_location (end_loc);
if (exploc_start.file != exploc_end.file)
{
error_at (end_loc, "start/end of RTL fragment are in different files");
return false;
}
if (exploc_start.line >= exploc_end.line)
{
error_at (end_loc,
"start of RTL fragment must be on an earlier line than end");
return false;
}
initialize_rtl ();
crtl->abi = &fndecl_abi (cfun->decl).base_abi ();
init_emit ();
init_varasm_status ();
function_reader reader;
if (!reader.read_file_fragment (exploc_start.file, exploc_start.line,
exploc_end.line - 1))
return false;
return true;
}
#if CHECKING_P
namespace selftest {
/* Verify that parse_edge_flags works. */
static void
test_edge_flags ()
{
/* parse_edge_flags modifies its input (due to strtok), so we must make
a copy of the literals. */
#define ASSERT_PARSE_EDGE_FLAGS(EXPECTED, STR) \
do { \
char *str = xstrdup (STR); \
ASSERT_EQ (EXPECTED, parse_edge_flags (str)); \
free (str); \
} while (0)
ASSERT_PARSE_EDGE_FLAGS (0, "");
ASSERT_PARSE_EDGE_FLAGS (EDGE_FALLTHRU, "FALLTHRU");
ASSERT_PARSE_EDGE_FLAGS (EDGE_ABNORMAL_CALL, "ABNORMAL_CALL");
ASSERT_PARSE_EDGE_FLAGS (EDGE_ABNORMAL | EDGE_ABNORMAL_CALL,
"ABNORMAL | ABNORMAL_CALL");
#undef ASSERT_PARSE_EDGE_FLAGS
}
/* Verify that lookup_reg_by_dump_name works. */
static void
test_parsing_regnos ()
{
ASSERT_EQ (-1, lookup_reg_by_dump_name ("this is not a register"));
/* Verify lookup of virtual registers. */
ASSERT_EQ (VIRTUAL_INCOMING_ARGS_REGNUM,
lookup_reg_by_dump_name ("virtual-incoming-args"));
ASSERT_EQ (VIRTUAL_STACK_VARS_REGNUM,
lookup_reg_by_dump_name ("virtual-stack-vars"));
ASSERT_EQ (VIRTUAL_STACK_DYNAMIC_REGNUM,
lookup_reg_by_dump_name ("virtual-stack-dynamic"));
ASSERT_EQ (VIRTUAL_OUTGOING_ARGS_REGNUM,
lookup_reg_by_dump_name ("virtual-outgoing-args"));
ASSERT_EQ (VIRTUAL_CFA_REGNUM,
lookup_reg_by_dump_name ("virtual-cfa"));
ASSERT_EQ (VIRTUAL_PREFERRED_STACK_BOUNDARY_REGNUM,
lookup_reg_by_dump_name ("virtual-preferred-stack-boundary"));
/* Verify lookup of non-virtual pseudos. */
ASSERT_EQ (LAST_VIRTUAL_REGISTER + 1, lookup_reg_by_dump_name ("<0>"));
ASSERT_EQ (LAST_VIRTUAL_REGISTER + 2, lookup_reg_by_dump_name ("<1>"));
}
/* Verify that edge E is as expected, with the src and dest basic blocks
having indices EXPECTED_SRC_IDX and EXPECTED_DEST_IDX respectively, and
the edge having flags equal to EXPECTED_FLAGS.
Use LOC as the effective location when reporting failures. */
static void
assert_edge_at (const location &loc, edge e, int expected_src_idx,
int expected_dest_idx, int expected_flags)
{
ASSERT_EQ_AT (loc, expected_src_idx, e->src->index);
ASSERT_EQ_AT (loc, expected_dest_idx, e->dest->index);
ASSERT_EQ_AT (loc, expected_flags, e->flags);
}
/* Verify that edge EDGE is as expected, with the src and dest basic blocks
having indices EXPECTED_SRC_IDX and EXPECTED_DEST_IDX respectively, and
the edge having flags equal to EXPECTED_FLAGS. */
#define ASSERT_EDGE(EDGE, EXPECTED_SRC_IDX, EXPECTED_DEST_IDX, \
EXPECTED_FLAGS) \
assert_edge_at (SELFTEST_LOCATION, EDGE, EXPECTED_SRC_IDX, \
EXPECTED_DEST_IDX, EXPECTED_FLAGS)
/* Verify that we can load RTL dumps. */
static void
test_loading_dump_fragment_1 ()
{
// TODO: filter on target?
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("asr_div1.rtl"));
/* Verify that the insns were loaded correctly. */
rtx_insn *insn_1 = get_insns ();
ASSERT_TRUE (insn_1);
ASSERT_EQ (1, INSN_UID (insn_1));
ASSERT_EQ (INSN, GET_CODE (insn_1));
ASSERT_EQ (SET, GET_CODE (PATTERN (insn_1)));
ASSERT_EQ (NULL, PREV_INSN (insn_1));
rtx_insn *insn_2 = NEXT_INSN (insn_1);
ASSERT_TRUE (insn_2);
ASSERT_EQ (2, INSN_UID (insn_2));
ASSERT_EQ (INSN, GET_CODE (insn_2));
ASSERT_EQ (insn_1, PREV_INSN (insn_2));
ASSERT_EQ (NULL, NEXT_INSN (insn_2));
/* Verify that registers were loaded correctly. */
rtx insn_1_dest = SET_DEST (PATTERN (insn_1));
ASSERT_EQ (REG, GET_CODE (insn_1_dest));
ASSERT_EQ ((LAST_VIRTUAL_REGISTER + 1) + 2, REGNO (insn_1_dest));
rtx insn_1_src = SET_SRC (PATTERN (insn_1));
ASSERT_EQ (LSHIFTRT, GET_CODE (insn_1_src));
rtx reg = XEXP (insn_1_src, 0);
ASSERT_EQ (REG, GET_CODE (reg));
ASSERT_EQ (LAST_VIRTUAL_REGISTER + 1, REGNO (reg));
/* Verify that get_insn_by_uid works. */
ASSERT_EQ (insn_1, get_insn_by_uid (1));
ASSERT_EQ (insn_2, get_insn_by_uid (2));
/* Verify that basic blocks were created. */
ASSERT_EQ (2, BLOCK_FOR_INSN (insn_1)->index);
ASSERT_EQ (2, BLOCK_FOR_INSN (insn_2)->index);
/* Verify that the CFG was recreated. */
ASSERT_TRUE (cfun);
verify_three_block_rtl_cfg (cfun);
basic_block bb2 = BASIC_BLOCK_FOR_FN (cfun, 2);
ASSERT_TRUE (bb2 != NULL);
ASSERT_EQ (BB_RTL, bb2->flags & BB_RTL);
ASSERT_EQ (2, bb2->index);
ASSERT_EQ (insn_1, BB_HEAD (bb2));
ASSERT_EQ (insn_2, BB_END (bb2));
}
/* Verify loading another RTL dump. */
static void
test_loading_dump_fragment_2 ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("simple-cse.rtl"));
rtx_insn *insn_1 = get_insn_by_uid (1);
rtx_insn *insn_2 = get_insn_by_uid (2);
rtx_insn *insn_3 = get_insn_by_uid (3);
rtx set1 = single_set (insn_1);
ASSERT_NE (NULL, set1);
rtx set2 = single_set (insn_2);
ASSERT_NE (NULL, set2);
rtx set3 = single_set (insn_3);
ASSERT_NE (NULL, set3);
rtx src1 = SET_SRC (set1);
ASSERT_EQ (PLUS, GET_CODE (src1));
rtx src2 = SET_SRC (set2);
ASSERT_EQ (PLUS, GET_CODE (src2));
/* Both src1 and src2 refer to "(reg:SI %0)".
Verify that we have pointer equality. */
rtx lhs1 = XEXP (src1, 0);
rtx lhs2 = XEXP (src2, 0);
ASSERT_EQ (lhs1, lhs2);
/* Verify that the CFG was recreated. */
ASSERT_TRUE (cfun);
verify_three_block_rtl_cfg (cfun);
}
/* Verify that CODE_LABEL insns are loaded correctly. */
static void
test_loading_labels ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("example-labels.rtl"));
rtx_insn *insn_100 = get_insn_by_uid (100);
ASSERT_EQ (CODE_LABEL, GET_CODE (insn_100));
ASSERT_EQ (100, INSN_UID (insn_100));
ASSERT_EQ (NULL, LABEL_NAME (insn_100));
ASSERT_EQ (0, LABEL_NUSES (insn_100));
ASSERT_EQ (30, CODE_LABEL_NUMBER (insn_100));
rtx_insn *insn_200 = get_insn_by_uid (200);
ASSERT_EQ (CODE_LABEL, GET_CODE (insn_200));
ASSERT_EQ (200, INSN_UID (insn_200));
ASSERT_STREQ ("some_label_name", LABEL_NAME (insn_200));
ASSERT_EQ (0, LABEL_NUSES (insn_200));
ASSERT_EQ (40, CODE_LABEL_NUMBER (insn_200));
/* Ensure that the presence of CODE_LABEL_NUMBER == 40
means that the next label num to be handed out will be 41. */
ASSERT_EQ (41, max_label_num ());
/* Ensure that label names read from a dump are GC-managed
and are found through the insn. */
forcibly_ggc_collect ();
ASSERT_TRUE (ggc_marked_p (insn_200));
ASSERT_TRUE (ggc_marked_p (LABEL_NAME (insn_200)));
}
/* Verify that the loader copes with an insn with a mode. */
static void
test_loading_insn_with_mode ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("insn-with-mode.rtl"));
rtx_insn *insn = get_insns ();
ASSERT_EQ (INSN, GET_CODE (insn));
/* Verify that the "TI" mode was set from "insn:TI". */
ASSERT_EQ (TImode, GET_MODE (insn));
}
/* Verify that the loader copes with a jump_insn to a label_ref. */
static void
test_loading_jump_to_label_ref ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("jump-to-label-ref.rtl"));
rtx_insn *jump_insn = get_insn_by_uid (1);
ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
rtx_insn *barrier = get_insn_by_uid (2);
ASSERT_EQ (BARRIER, GET_CODE (barrier));
rtx_insn *code_label = get_insn_by_uid (100);
ASSERT_EQ (CODE_LABEL, GET_CODE (code_label));
/* Verify the jump_insn. */
ASSERT_EQ (4, BLOCK_FOR_INSN (jump_insn)->index);
ASSERT_EQ (SET, GET_CODE (PATTERN (jump_insn)));
/* Ensure that the "(pc)" is using the global singleton. */
ASSERT_RTX_PTR_EQ (pc_rtx, SET_DEST (PATTERN (jump_insn)));
rtx label_ref = SET_SRC (PATTERN (jump_insn));
ASSERT_EQ (LABEL_REF, GET_CODE (label_ref));
ASSERT_EQ (code_label, label_ref_label (label_ref));
ASSERT_EQ (code_label, JUMP_LABEL (jump_insn));
/* Verify the code_label. */
ASSERT_EQ (5, BLOCK_FOR_INSN (code_label)->index);
ASSERT_EQ (NULL, LABEL_NAME (code_label));
ASSERT_EQ (1, LABEL_NUSES (code_label));
/* Verify the generated CFG. */
/* Locate blocks. */
basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun);
ASSERT_TRUE (entry != NULL);
ASSERT_EQ (ENTRY_BLOCK, entry->index);
basic_block exit = EXIT_BLOCK_PTR_FOR_FN (cfun);
ASSERT_TRUE (exit != NULL);
ASSERT_EQ (EXIT_BLOCK, exit->index);
basic_block bb4 = (*cfun->cfg->x_basic_block_info)[4];
basic_block bb5 = (*cfun->cfg->x_basic_block_info)[5];
ASSERT_EQ (4, bb4->index);
ASSERT_EQ (5, bb5->index);
/* Entry block. */
ASSERT_EQ (NULL, entry->preds);
ASSERT_EQ (1, entry->succs->length ());
ASSERT_EDGE ((*entry->succs)[0], 0, 4, EDGE_FALLTHRU);
/* bb4. */
ASSERT_EQ (1, bb4->preds->length ());
ASSERT_EDGE ((*bb4->preds)[0], 0, 4, EDGE_FALLTHRU);
ASSERT_EQ (1, bb4->succs->length ());
ASSERT_EDGE ((*bb4->succs)[0], 4, 5, 0x0);
/* bb5. */
ASSERT_EQ (1, bb5->preds->length ());
ASSERT_EDGE ((*bb5->preds)[0], 4, 5, 0x0);
ASSERT_EQ (1, bb5->succs->length ());
ASSERT_EDGE ((*bb5->succs)[0], 5, 1, EDGE_FALLTHRU);
/* Exit block. */
ASSERT_EQ (1, exit->preds->length ());
ASSERT_EDGE ((*exit->preds)[0], 5, 1, EDGE_FALLTHRU);
ASSERT_EQ (NULL, exit->succs);
}
/* Verify that the loader copes with a jump_insn to a label_ref
marked "return". */
static void
test_loading_jump_to_return ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("jump-to-return.rtl"));
rtx_insn *jump_insn = get_insn_by_uid (1);
ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
ASSERT_RTX_PTR_EQ (ret_rtx, JUMP_LABEL (jump_insn));
}
/* Verify that the loader copes with a jump_insn to a label_ref
marked "simple_return". */
static void
test_loading_jump_to_simple_return ()
{
rtl_dump_test t (SELFTEST_LOCATION,
locate_file ("jump-to-simple-return.rtl"));
rtx_insn *jump_insn = get_insn_by_uid (1);
ASSERT_EQ (JUMP_INSN, GET_CODE (jump_insn));
ASSERT_RTX_PTR_EQ (simple_return_rtx, JUMP_LABEL (jump_insn));
}
/* Verify that the loader copes with a NOTE_INSN_BASIC_BLOCK. */
static void
test_loading_note_insn_basic_block ()
{
rtl_dump_test t (SELFTEST_LOCATION,
locate_file ("note_insn_basic_block.rtl"));
rtx_insn *note = get_insn_by_uid (1);
ASSERT_EQ (NOTE, GET_CODE (note));
ASSERT_EQ (2, BLOCK_FOR_INSN (note)->index);
ASSERT_EQ (NOTE_INSN_BASIC_BLOCK, NOTE_KIND (note));
ASSERT_EQ (2, NOTE_BASIC_BLOCK (note)->index);
ASSERT_EQ (BASIC_BLOCK_FOR_FN (cfun, 2), NOTE_BASIC_BLOCK (note));
}
/* Verify that the loader copes with a NOTE_INSN_DELETED. */
static void
test_loading_note_insn_deleted ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("note-insn-deleted.rtl"));
rtx_insn *note = get_insn_by_uid (1);
ASSERT_EQ (NOTE, GET_CODE (note));
ASSERT_EQ (NOTE_INSN_DELETED, NOTE_KIND (note));
}
/* Verify that the const_int values are consolidated, since
pointer equality corresponds to value equality.
TODO: do this for all in CASE_CONST_UNIQUE. */
static void
test_loading_const_int ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("const-int.rtl"));
/* Verify that const_int values below MAX_SAVED_CONST_INT use
the global values. */
ASSERT_EQ (const0_rtx, SET_SRC (PATTERN (get_insn_by_uid (1))));
ASSERT_EQ (const1_rtx, SET_SRC (PATTERN (get_insn_by_uid (2))));
ASSERT_EQ (constm1_rtx, SET_SRC (PATTERN (get_insn_by_uid (3))));
/* Verify that other const_int values are consolidated. */
rtx int256 = gen_rtx_CONST_INT (SImode, 256);
ASSERT_EQ (int256, SET_SRC (PATTERN (get_insn_by_uid (4))));
}
/* Verify that the loader copes with a SYMBOL_REF. */
static void
test_loading_symbol_ref ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("symbol-ref.rtl"));
rtx_insn *insn = get_insns ();
rtx high = SET_SRC (PATTERN (insn));
ASSERT_EQ (HIGH, GET_CODE (high));
rtx symbol_ref = XEXP (high, 0);
ASSERT_EQ (SYMBOL_REF, GET_CODE (symbol_ref));
/* Verify that "[flags 0xc0]" was parsed. */
ASSERT_EQ (0xc0, SYMBOL_REF_FLAGS (symbol_ref));
/* TODO: we don't yet load SYMBOL_REF_DECL. */
}
/* Verify that the loader can rebuild a CFG. */
static void
test_loading_cfg ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("cfg-test.rtl"));
ASSERT_STREQ ("cfg_test", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
ASSERT_TRUE (cfun);
ASSERT_TRUE (cfun->cfg != NULL);
ASSERT_EQ (6, n_basic_blocks_for_fn (cfun));
ASSERT_EQ (6, n_edges_for_fn (cfun));
/* The "fake" basic blocks. */
basic_block entry = ENTRY_BLOCK_PTR_FOR_FN (cfun);
ASSERT_TRUE (entry != NULL);
ASSERT_EQ (ENTRY_BLOCK, entry->index);
basic_block exit = EXIT_BLOCK_PTR_FOR_FN (cfun);
ASSERT_TRUE (exit != NULL);
ASSERT_EQ (EXIT_BLOCK, exit->index);
/* The "real" basic blocks. */
basic_block bb2 = (*cfun->cfg->x_basic_block_info)[2];
basic_block bb3 = (*cfun->cfg->x_basic_block_info)[3];
basic_block bb4 = (*cfun->cfg->x_basic_block_info)[4];
basic_block bb5 = (*cfun->cfg->x_basic_block_info)[5];
ASSERT_EQ (2, bb2->index);
ASSERT_EQ (3, bb3->index);
ASSERT_EQ (4, bb4->index);
ASSERT_EQ (5, bb5->index);
/* Verify connectivity. */
/* Entry block. */
ASSERT_EQ (NULL, entry->preds);
ASSERT_EQ (1, entry->succs->length ());
ASSERT_EDGE ((*entry->succs)[0], 0, 2, EDGE_FALLTHRU);
/* bb2. */
ASSERT_EQ (1, bb2->preds->length ());
ASSERT_EDGE ((*bb2->preds)[0], 0, 2, EDGE_FALLTHRU);
ASSERT_EQ (2, bb2->succs->length ());
ASSERT_EDGE ((*bb2->succs)[0], 2, 3, EDGE_TRUE_VALUE);
ASSERT_EDGE ((*bb2->succs)[1], 2, 4, EDGE_FALSE_VALUE);
/* bb3. */
ASSERT_EQ (1, bb3->preds->length ());
ASSERT_EDGE ((*bb3->preds)[0], 2, 3, EDGE_TRUE_VALUE);
ASSERT_EQ (1, bb3->succs->length ());
ASSERT_EDGE ((*bb3->succs)[0], 3, 5, EDGE_FALLTHRU);
/* bb4. */
ASSERT_EQ (1, bb4->preds->length ());
ASSERT_EDGE ((*bb4->preds)[0], 2, 4, EDGE_FALSE_VALUE);
ASSERT_EQ (1, bb4->succs->length ());
ASSERT_EDGE ((*bb4->succs)[0], 4, 5, EDGE_FALLTHRU);
/* bb5. */
ASSERT_EQ (2, bb5->preds->length ());
ASSERT_EDGE ((*bb5->preds)[0], 3, 5, EDGE_FALLTHRU);
ASSERT_EDGE ((*bb5->preds)[1], 4, 5, EDGE_FALLTHRU);
ASSERT_EQ (1, bb5->succs->length ());
ASSERT_EDGE ((*bb5->succs)[0], 5, 1, EDGE_FALLTHRU);
/* Exit block. */
ASSERT_EQ (1, exit->preds->length ());
ASSERT_EDGE ((*exit->preds)[0], 5, 1, EDGE_FALLTHRU);
ASSERT_EQ (NULL, exit->succs);
}
/* Verify that the loader copes with sparse block indices.
This testcase loads a file with a "(block 42)". */
static void
test_loading_bb_index ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("bb-index.rtl"));
ASSERT_STREQ ("test_bb_index", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
ASSERT_TRUE (cfun);
ASSERT_TRUE (cfun->cfg != NULL);
ASSERT_EQ (3, n_basic_blocks_for_fn (cfun));
ASSERT_EQ (43, basic_block_info_for_fn (cfun)->length ());
ASSERT_EQ (2, n_edges_for_fn (cfun));
ASSERT_EQ (NULL, (*cfun->cfg->x_basic_block_info)[41]);
basic_block bb42 = (*cfun->cfg->x_basic_block_info)[42];
ASSERT_NE (NULL, bb42);
ASSERT_EQ (42, bb42->index);
}
/* Verify that function_reader::handle_any_trailing_information correctly
parses all the possible items emitted for a MEM. */
static void
test_loading_mem ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("mem.rtl"));
ASSERT_STREQ ("test_mem", IDENTIFIER_POINTER (DECL_NAME (cfun->decl)));
ASSERT_TRUE (cfun);
/* Verify parsing of "[42 i+17 S8 A128 AS5]". */
rtx_insn *insn_1 = get_insn_by_uid (1);
rtx set1 = single_set (insn_1);
rtx mem1 = SET_DEST (set1);
ASSERT_EQ (42, MEM_ALIAS_SET (mem1));
/* "+17". */
ASSERT_TRUE (MEM_OFFSET_KNOWN_P (mem1));
ASSERT_KNOWN_EQ (17, MEM_OFFSET (mem1));
/* "S8". */
ASSERT_KNOWN_EQ (8, MEM_SIZE (mem1));
/* "A128. */
ASSERT_EQ (128, MEM_ALIGN (mem1));
/* "AS5. */
ASSERT_EQ (5, MEM_ADDR_SPACE (mem1));
/* Verify parsing of "43 i+18 S9 AS6"
(an address space without an alignment). */
rtx_insn *insn_2 = get_insn_by_uid (2);
rtx set2 = single_set (insn_2);
rtx mem2 = SET_DEST (set2);
ASSERT_EQ (43, MEM_ALIAS_SET (mem2));
/* "+18". */
ASSERT_TRUE (MEM_OFFSET_KNOWN_P (mem2));
ASSERT_KNOWN_EQ (18, MEM_OFFSET (mem2));
/* "S9". */
ASSERT_KNOWN_EQ (9, MEM_SIZE (mem2));
/* "AS6. */
ASSERT_EQ (6, MEM_ADDR_SPACE (mem2));
}
/* Verify that "repeated xN" is read correctly. */
static void
test_loading_repeat ()
{
rtl_dump_test t (SELFTEST_LOCATION, locate_file ("repeat.rtl"));
rtx_insn *insn_1 = get_insn_by_uid (1);
ASSERT_EQ (PARALLEL, GET_CODE (PATTERN (insn_1)));
ASSERT_EQ (64, XVECLEN (PATTERN (insn_1), 0));
for (int i = 0; i < 64; i++)
ASSERT_EQ (const0_rtx, XVECEXP (PATTERN (insn_1), 0, i));
}
/* Run all of the selftests within this file. */
void
read_rtl_function_c_tests ()
{
test_edge_flags ();
test_parsing_regnos ();
test_loading_dump_fragment_1 ();
test_loading_dump_fragment_2 ();
test_loading_labels ();
test_loading_insn_with_mode ();
test_loading_jump_to_label_ref ();
test_loading_jump_to_return ();
test_loading_jump_to_simple_return ();
test_loading_note_insn_basic_block ();
test_loading_note_insn_deleted ();
test_loading_const_int ();
test_loading_symbol_ref ();
test_loading_cfg ();
test_loading_bb_index ();
test_loading_mem ();
test_loading_repeat ();
}
} // namespace selftest
#endif /* #if CHECKING_P */