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.
2228 lines
61 KiB
C
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 */
|