/* Discovery of auto-inc and auto-dec instructions.
- Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
+ Copyright (C) 2006-2016 Free Software Foundation, Inc.
Contributed by Kenneth Zadeck <zadeck@naturalbridge.com>
This file is part of GCC.
#include "config.h"
#include "system.h"
#include "coretypes.h"
-#include "tm.h"
-#include "tree.h"
+#include "backend.h"
+#include "target.h"
#include "rtl.h"
-#include "tm_p.h"
-#include "hard-reg-set.h"
-#include "basic-block.h"
+#include "tree.h"
+#include "predict.h"
+#include "df.h"
#include "insn-config.h"
-#include "regs.h"
-#include "flags.h"
-#include "output.h"
-#include "function.h"
-#include "except.h"
-#include "toplev.h"
+#include "emit-rtl.h"
#include "recog.h"
+#include "cfgrtl.h"
#include "expr.h"
-#include "timevar.h"
#include "tree-pass.h"
-#include "df.h"
#include "dbgcnt.h"
-#include "target.h"
+#include "print-rtl.h"
/* This pass was originally removed from flow.c. However there is
almost nothing that remains of that code.
before the ref or +c if the increment was after the ref, then if we
can do the combination but switch the pre/post bit. */
-#ifdef AUTO_INC_DEC
enum form
{
static struct inc_insn
{
- rtx insn; /* The insn being parsed. */
+ rtx_insn *insn; /* The insn being parsed. */
rtx pat; /* The pattern of the insn. */
bool reg1_is_const; /* True if reg1 is const, false if reg1 is a reg. */
enum form form;
static struct mem_insn
{
- rtx insn; /* The insn being parsed. */
+ rtx_insn *insn; /* The insn being parsed. */
rtx pat; /* The pattern of the insn. */
rtx *mem_loc; /* The address of the field that holds the mem */
/* that is to be replaced. */
must be compared with the current block.
*/
-static rtx *reg_next_use = NULL;
-static rtx *reg_next_inc_use = NULL;
-static rtx *reg_next_def = NULL;
+static rtx_insn **reg_next_use = NULL;
+static rtx_insn **reg_next_inc_use = NULL;
+static rtx_insn **reg_next_def = NULL;
/* Move dead note that match PATTERN to TO_INSN from FROM_INSN. We do
does not appear that there are any other kinds of relevant notes. */
static void
-move_dead_notes (rtx to_insn, rtx from_insn, rtx pattern)
+move_dead_notes (rtx_insn *to_insn, rtx_insn *from_insn, rtx pattern)
{
rtx note;
rtx next_note;
}
}
-
-/* Create a mov insn DEST_REG <- SRC_REG and insert it before
- NEXT_INSN. */
-
-static rtx
-insert_move_insn_before (rtx next_insn, rtx dest_reg, rtx src_reg)
-{
- rtx insns;
-
- start_sequence ();
- emit_move_insn (dest_reg, src_reg);
- insns = get_insns ();
- end_sequence ();
- emit_insn_before (insns, next_insn);
- return insns;
-}
-
-
/* Change mem_insn.mem_loc so that uses NEW_ADDR which has an
increment of INC_REG. To have reached this point, the change is a
legitimate one from a dataflow point of view. The only questions
handled mov free. */
basic_block bb = BLOCK_FOR_INSN (mem_insn.insn);
- rtx mov_insn = NULL;
+ rtx_insn *mov_insn = NULL;
int regno;
rtx mem = *mem_insn.mem_loc;
- enum machine_mode mode = GET_MODE (mem);
+ machine_mode mode = GET_MODE (mem);
rtx new_mem;
int old_cost = 0;
int new_cost = 0;
PUT_MODE (mem_tmp, mode);
XEXP (mem_tmp, 0) = new_addr;
- old_cost = (rtx_cost (mem, SET, speed)
- + rtx_cost (PATTERN (inc_insn.insn), SET, speed));
- new_cost = rtx_cost (mem_tmp, SET, speed);
+ old_cost = (set_src_cost (mem, mode, speed)
+ + set_rtx_cost (PATTERN (inc_insn.insn), speed));
+
+ new_cost = set_src_cost (mem_tmp, mode, speed);
+
+ /* In the FORM_PRE_ADD and FORM_POST_ADD cases we emit an extra move
+ whose cost we should account for. */
+ if (inc_insn.form == FORM_PRE_ADD
+ || inc_insn.form == FORM_POST_ADD)
+ {
+ start_sequence ();
+ emit_move_insn (inc_insn.reg_res, inc_insn.reg0);
+ mov_insn = get_insns ();
+ end_sequence ();
+ new_cost += seq_cost (mov_insn, speed);
+ }
/* The first item of business is to see if this is profitable. */
if (old_cost < new_cost)
return false;
}
- /* Jump thru a lot of hoops to keep the attributes up to date. We
+ /* Jump through a lot of hoops to keep the attributes up to date. We
do not want to call one of the change address variants that take
an offset even though we know the offset in many cases. These
assume you are changing where the address is pointing by the
/* Replace the addition with a move. Do it at the location of
the addition since the operand of the addition may change
before the memory reference. */
- mov_insn = insert_move_insn_before (inc_insn.insn,
- inc_insn.reg_res, inc_insn.reg0);
+ gcc_assert (mov_insn);
+ emit_insn_before (mov_insn, inc_insn.insn);
move_dead_notes (mov_insn, inc_insn.insn, inc_insn.reg0);
regno = REGNO (inc_insn.reg_res);
break;
case FORM_POST_ADD:
- mov_insn = insert_move_insn_before (mem_insn.insn,
- inc_insn.reg_res, inc_insn.reg0);
+ gcc_assert (mov_insn);
+ emit_insn_before (mov_insn, mem_insn.insn);
move_dead_notes (mov_insn, inc_insn.insn, inc_insn.reg0);
/* Do not move anything to the mov insn because the instruction
/* The width of the mem being accessed. */
int size = GET_MODE_SIZE (GET_MODE (mem));
- rtx last_insn = NULL;
- enum machine_mode reg_mode = GET_MODE (inc_reg);
+ rtx_insn *last_insn = NULL;
+ machine_mode reg_mode = GET_MODE (inc_reg);
switch (inc_insn.form)
{
NEXT_ARRAY) or defines (if reg_next_def is passed in NEXT_ARRAY)
REGNO in BB. */
-static rtx
-get_next_ref (int regno, basic_block bb, rtx *next_array)
+static rtx_insn *
+get_next_ref (int regno, basic_block bb, rtx_insn **next_array)
{
- rtx insn = next_array[regno];
+ rtx_insn *insn = next_array[regno];
/* Lazy about cleaning out the next_arrays. */
if (insn && BLOCK_FOR_INSN (insn) != bb)
}
-/* Reverse the operands in a mem insn. */
-
-static void
-reverse_mem (void)
-{
- rtx tmp = mem_insn.reg1;
- mem_insn.reg1 = mem_insn.reg0;
- mem_insn.reg0 = tmp;
-}
-
-
-/* Reverse the operands in a inc insn. */
-
-static void
-reverse_inc (void)
-{
- rtx tmp = inc_insn.reg1;
- inc_insn.reg1 = inc_insn.reg0;
- inc_insn.reg0 = tmp;
-}
-
-
/* Return true if INSN is of a form "a = b op c" where a and b are
regs. op is + if c is a reg and +|- if c is a const. Fill in
INC_INSN with what is found.
processed. */
static bool
-parse_add_or_inc (rtx insn, bool before_mem)
+parse_add_or_inc (rtx_insn *insn, bool before_mem)
{
rtx pat = single_set (insn);
if (!pat)
{
/* Reverse the two operands and turn *_ADD into *_INC since
a = c + a. */
- reverse_inc ();
+ std::swap (inc_insn.reg0, inc_insn.reg1);
inc_insn.form = before_mem ? FORM_PRE_INC : FORM_POST_INC;
return true;
}
static bool
find_inc (bool first_try)
{
- rtx insn;
+ rtx_insn *insn;
basic_block bb = BLOCK_FOR_INSN (mem_insn.insn);
- rtx other_insn;
- df_ref *def_rec;
+ rtx_insn *other_insn;
+ df_ref def;
/* Make sure this reg appears only once in this insn. */
if (count_occurrences (PATTERN (mem_insn.insn), mem_insn.reg0, 1) != 1)
find this. Only try it once though. */
if (first_try && !mem_insn.reg1_is_const)
{
- reverse_mem ();
+ std::swap (mem_insn.reg0, mem_insn.reg1);
return find_inc (false);
}
else
/* Need to assure that none of the operands of the inc instruction are
assigned to by the mem insn. */
- for (def_rec = DF_INSN_DEFS (mem_insn.insn); *def_rec; def_rec++)
+ FOR_EACH_INSN_DEF (def, mem_insn.insn)
{
- df_ref def = *def_rec;
unsigned int regno = DF_REF_REGNO (def);
if ((regno == REGNO (inc_insn.reg0))
|| (regno == REGNO (inc_insn.reg_res)))
{
/* Make sure that there is no insn that assigns to inc_insn.res
between the mem_insn and the inc_insn. */
- rtx other_insn = get_next_ref (REGNO (inc_insn.reg_res),
- BLOCK_FOR_INSN (mem_insn.insn),
- reg_next_def);
+ rtx_insn *other_insn = get_next_ref (REGNO (inc_insn.reg_res),
+ BLOCK_FOR_INSN (mem_insn.insn),
+ reg_next_def);
if (other_insn != inc_insn.insn)
{
if (dump_file)
/* For the post_add to work, the result_reg of the inc must not be
used in the mem insn since this will become the new index
register. */
- if (count_occurrences (PATTERN (mem_insn.insn), inc_insn.reg_res, 1) != 0)
+ if (reg_overlap_mentioned_p (inc_insn.reg_res, PATTERN (mem_insn.insn)))
{
if (dump_file)
fprintf (dump_file, "base reg replacement failure.\n");
return false;
if (!rtx_equal_p (mem_insn.reg0, inc_insn.reg0))
- reverse_inc ();
+ std::swap (inc_insn.reg0, inc_insn.reg1);
}
other_insn
then we just abandon this. */
int luid = DF_INSN_LUID (inc_insn.insn);
- rtx other_insn;
+ rtx_insn *other_insn;
/* Make sure this reg appears only once in this insn. */
if (count_occurrences (PATTERN (mem_insn.insn), mem_insn.reg1, 1) != 1)
/* See comment above on find_inc (false) call. */
if (first_try)
{
- reverse_mem ();
+ std::swap (mem_insn.reg0, mem_insn.reg1);
return find_inc (false);
}
else
{
/* We know that mem_insn.reg0 must equal inc_insn.reg1
or else we would not have found the inc insn. */
- reverse_mem ();
+ std::swap (mem_insn.reg0, mem_insn.reg1);
if (!rtx_equal_p (mem_insn.reg0, inc_insn.reg0))
{
/* See comment above on find_inc (false) call. */
{
if (first_try)
{
- reverse_mem ();
+ std::swap (mem_insn.reg0, mem_insn.reg1);
return find_inc (false);
}
else
static void
merge_in_block (int max_reg, basic_block bb)
{
- rtx insn;
- rtx curr;
+ rtx_insn *insn;
+ rtx_insn *curr;
int success_in_block = 0;
if (dump_file)
FOR_BB_INSNS_REVERSE_SAFE (bb, insn, curr)
{
- unsigned int uid = INSN_UID (insn);
bool insn_is_add_or_inc = true;
if (!NONDEBUG_INSN_P (insn))
clear of c because the inc insn is going to move
into the mem_insn.insn. */
int luid = DF_INSN_LUID (mem_insn.insn);
- rtx other_insn
+ rtx_insn *other_insn
= get_next_ref (REGNO (inc_insn.reg1), bb, reg_next_use);
if (other_insn && luid > DF_INSN_LUID (other_insn))
/* If the inc insn was merged with a mem, the inc insn is gone
and there is noting to update. */
- if (DF_INSN_UID_GET (uid))
+ if (df_insn_info *insn_info = DF_INSN_INFO_GET (insn))
{
- df_ref *def_rec;
- df_ref *use_rec;
+ df_ref def, use;
+
/* Need to update next use. */
- for (def_rec = DF_INSN_UID_DEFS (uid); *def_rec; def_rec++)
+ FOR_EACH_INSN_INFO_DEF (def, insn_info)
{
- df_ref def = *def_rec;
reg_next_use[DF_REF_REGNO (def)] = NULL;
reg_next_inc_use[DF_REF_REGNO (def)] = NULL;
reg_next_def[DF_REF_REGNO (def)] = insn;
}
- for (use_rec = DF_INSN_UID_USES (uid); *use_rec; use_rec++)
+ FOR_EACH_INSN_INFO_USE (use, insn_info)
{
- df_ref use = *use_rec;
reg_next_use[DF_REF_REGNO (use)] = insn;
if (insn_is_add_or_inc)
reg_next_inc_use[DF_REF_REGNO (use)] = insn;
}
}
else if (dump_file)
- fprintf (dump_file, "skipping update of deleted insn %d\n", uid);
+ fprintf (dump_file, "skipping update of deleted insn %d\n",
+ INSN_UID (insn));
}
/* If we were successful, try again. There may have been several
{
/* In this case, we must clear these vectors since the trick of
testing if the stale insn in the block will not work. */
- memset (reg_next_use, 0, max_reg * sizeof(rtx));
- memset (reg_next_inc_use, 0, max_reg * sizeof(rtx));
- memset (reg_next_def, 0, max_reg * sizeof(rtx));
+ memset (reg_next_use, 0, max_reg * sizeof (rtx));
+ memset (reg_next_inc_use, 0, max_reg * sizeof (rtx));
+ memset (reg_next_def, 0, max_reg * sizeof (rtx));
df_recompute_luids (bb);
merge_in_block (max_reg, bb);
}
}
-#endif
+/* Discover auto-inc auto-dec instructions. */
+
+namespace {
+
+const pass_data pass_data_inc_dec =
+{
+ RTL_PASS, /* type */
+ "auto_inc_dec", /* name */
+ OPTGROUP_NONE, /* optinfo_flags */
+ TV_AUTO_INC_DEC, /* tv_id */
+ 0, /* properties_required */
+ 0, /* properties_provided */
+ 0, /* properties_destroyed */
+ 0, /* todo_flags_start */
+ TODO_df_finish, /* todo_flags_finish */
+};
-static unsigned int
-rest_of_handle_auto_inc_dec (void)
+class pass_inc_dec : public rtl_opt_pass
{
-#ifdef AUTO_INC_DEC
+public:
+ pass_inc_dec (gcc::context *ctxt)
+ : rtl_opt_pass (pass_data_inc_dec, ctxt)
+ {}
+
+ /* opt_pass methods: */
+ virtual bool gate (function *)
+ {
+ if (!AUTO_INC_DEC)
+ return false;
+
+ return (optimize > 0 && flag_auto_inc_dec);
+ }
+
+
+ unsigned int execute (function *);
+
+}; // class pass_inc_dec
+
+unsigned int
+pass_inc_dec::execute (function *fun ATTRIBUTE_UNUSED)
+{
+ if (!AUTO_INC_DEC)
+ return 0;
+
basic_block bb;
int max_reg = max_reg_num ();
df_note_add_problem ();
df_analyze ();
- reg_next_use = XCNEWVEC (rtx, max_reg);
- reg_next_inc_use = XCNEWVEC (rtx, max_reg);
- reg_next_def = XCNEWVEC (rtx, max_reg);
- FOR_EACH_BB (bb)
+ reg_next_use = XCNEWVEC (rtx_insn *, max_reg);
+ reg_next_inc_use = XCNEWVEC (rtx_insn *, max_reg);
+ reg_next_def = XCNEWVEC (rtx_insn *, max_reg);
+ FOR_EACH_BB_FN (bb, fun)
merge_in_block (max_reg, bb);
free (reg_next_use);
free (reg_next_def);
mem_tmp = NULL;
-#endif
+
return 0;
}
+} // anon namespace
-/* Discover auto-inc auto-dec instructions. */
-
-static bool
-gate_auto_inc_dec (void)
+rtl_opt_pass *
+make_pass_inc_dec (gcc::context *ctxt)
{
-#ifdef AUTO_INC_DEC
- return (optimize > 0 && flag_auto_inc_dec);
-#else
- return false;
-#endif
+ return new pass_inc_dec (ctxt);
}
-
-
-struct rtl_opt_pass pass_inc_dec =
-{
- {
- RTL_PASS,
- "auto_inc_dec", /* name */
- gate_auto_inc_dec, /* gate */
- rest_of_handle_auto_inc_dec, /* execute */
- NULL, /* sub */
- NULL, /* next */
- 0, /* static_pass_number */
- TV_AUTO_INC_DEC, /* tv_id */
- 0, /* properties_required */
- 0, /* properties_provided */
- 0, /* properties_destroyed */
- 0, /* todo_flags_start */
- TODO_dump_func |
- TODO_df_finish, /* todo_flags_finish */
- }
-};