/* Move registers around to reduce number of move instructions needed.
- Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997,
+ 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 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 2, or (at your option) any later
+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
for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
/* This module looks for cases where matching constraints would force
#include "reload.h"
#include "timevar.h"
#include "tree-pass.h"
-
+#include "df.h"
static int perhaps_ends_bb_p (rtx);
static int optimize_reg_copy_1 (rtx, rtx, rtx);
static rtx discover_flags_reg (void);
static void mark_flags_life_zones (rtx);
-static void flags_set_1 (rtx, rtx, void *);
+static void flags_set_1 (rtx, const_rtx, void *);
static int try_auto_increment (rtx, rtx, rtx, rtx, HOST_WIDE_INT, int);
static int find_matches (rtx, struct match *);
&& ! CLASS_LIKELY_SPILLED_P (class1)));
}
+/* Find the place in the rtx X where REG is used as a memory address.
+ Return the MEM rtx that so uses it.
+ If PLUSCONST is nonzero, search instead for a memory address equivalent to
+ (plus REG (const_int PLUSCONST)).
+
+ If such an address does not appear, return 0.
+ If REG appears more than once, or is used other than in such an address,
+ return (rtx) 1. */
+
+static rtx
+find_use_as_address (rtx x, rtx reg, HOST_WIDE_INT plusconst)
+{
+ enum rtx_code code = GET_CODE (x);
+ const char * const fmt = GET_RTX_FORMAT (code);
+ int i;
+ rtx value = 0;
+ rtx tem;
+
+ if (code == MEM && XEXP (x, 0) == reg && plusconst == 0)
+ return x;
+
+ if (code == MEM && GET_CODE (XEXP (x, 0)) == PLUS
+ && XEXP (XEXP (x, 0), 0) == reg
+ && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+ && INTVAL (XEXP (XEXP (x, 0), 1)) == plusconst)
+ return x;
+
+ if (code == SIGN_EXTRACT || code == ZERO_EXTRACT)
+ {
+ /* If REG occurs inside a MEM used in a bit-field reference,
+ that is unacceptable. */
+ if (find_use_as_address (XEXP (x, 0), reg, 0) != 0)
+ return (rtx) (size_t) 1;
+ }
+
+ if (x == reg)
+ return (rtx) (size_t) 1;
+
+ for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
+ {
+ if (fmt[i] == 'e')
+ {
+ tem = find_use_as_address (XEXP (x, i), reg, plusconst);
+ if (value == 0)
+ value = tem;
+ else if (tem != 0)
+ return (rtx) (size_t) 1;
+ }
+ else if (fmt[i] == 'E')
+ {
+ int j;
+ for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+ {
+ tem = find_use_as_address (XVECEXP (x, i, j), reg, plusconst);
+ if (value == 0)
+ value = tem;
+ else if (tem != 0)
+ return (rtx) (size_t) 1;
+ }
+ }
+ }
+
+ return value;
+}
+
+
/* INC_INSN is an instruction that adds INCREMENT to REG.
Try to fold INC_INSN as a post/pre in/decrement into INSN.
Iff INC_INSN_SET is nonzero, inc_insn has a destination different from src.
/* If there is a REG_DEAD note on this insn, we must
change this not to REG_UNUSED meaning that the register
is set, but the value is dead. Failure to do so will
- result in a sched1 dieing -- when it recomputes lifetime
+ result in sched1 dying -- when it recomputes lifetime
information, the number of REG_DEAD notes will have
changed. */
rtx note = find_reg_note (insn, REG_DEAD, reg);
{
int i;
for (i = 0; i < flags_nregs; ++i)
- live |= REGNO_REG_SET_P (block->il.rtl->global_live_at_start,
- flags_regno + i);
+ live |= REGNO_REG_SET_P (df_get_live_in (block), flags_regno + i);
}
#endif
/* A subroutine of mark_flags_life_zones, called through note_stores. */
static void
-flags_set_1 (rtx x, rtx pat, void *data ATTRIBUTE_UNUSED)
+flags_set_1 (rtx x, const_rtx pat, void *data ATTRIBUTE_UNUSED)
{
if (GET_CODE (pat) == SET
&& reg_overlap_mentioned_p (x, flags_set_1_rtx))
int s_length = 0;
int d_n_calls = 0;
int s_n_calls = 0;
+ int s_freq_calls = 0;
+ int d_freq_calls = 0;
/* We can do the optimization. Scan forward from INSN again,
replacing regs as we go. Set FAILED if a replacement can't
/* Similarly, total calls for SREGNO, total calls beyond
the death note for DREGNO. */
s_n_calls++;
+ s_freq_calls += REG_FREQ_FROM_BB (BLOCK_FOR_INSN (q));
if (dest_death)
- d_n_calls++;
+ {
+ d_n_calls++;
+ d_freq_calls += REG_FREQ_FROM_BB (BLOCK_FOR_INSN (q));
+ }
}
/* If DEST dies here, remove the death note and save it for
}
REG_N_CALLS_CROSSED (sregno) -= s_n_calls;
+ REG_FREQ_CALLS_CROSSED (sregno) -= s_freq_calls;
}
/* Move death note of SRC from P to INSN. */
if (REG_LIVE_LENGTH (dregno) >= 0)
REG_LIVE_LENGTH (dregno) += d_length;
REG_N_CALLS_CROSSED (dregno) += d_n_calls;
+ REG_FREQ_CALLS_CROSSED (dregno) += d_freq_calls;
}
}
if (INSN_P (q))
{
if (reg_mentioned_p (dest, PATTERN (q)))
- PATTERN (q) = replace_rtx (PATTERN (q), dest, src);
+ {
+ PATTERN (q) = replace_rtx (PATTERN (q), dest, src);
+ df_insn_rescan (q);
+ }
if (CALL_P (q))
{
+ int freq = REG_FREQ_FROM_BB (BLOCK_FOR_INSN (q));
REG_N_CALLS_CROSSED (dregno)--;
REG_N_CALLS_CROSSED (sregno)++;
+ REG_FREQ_CALLS_CROSSED (dregno) -= freq;
+ REG_FREQ_CALLS_CROSSED (sregno) += freq;
}
}
rtx *p_move_notes;
int src_regno;
int dest_regno;
- int bb;
int insn_uid;
int move_uid;
/* Update the various register tables. */
dest_regno = REGNO (dest);
- REG_N_SETS (dest_regno) ++;
+ INC_REG_N_SETS (dest_regno, 1);
REG_LIVE_LENGTH (dest_regno)++;
- if (REGNO_FIRST_UID (dest_regno) == insn_uid)
- REGNO_FIRST_UID (dest_regno) = move_uid;
-
src_regno = REGNO (src);
if (! find_reg_note (move_insn, REG_DEAD, src))
REG_LIVE_LENGTH (src_regno)++;
-
- if (REGNO_FIRST_UID (src_regno) == insn_uid)
- REGNO_FIRST_UID (src_regno) = move_uid;
-
- if (REGNO_LAST_UID (src_regno) == insn_uid)
- REGNO_LAST_UID (src_regno) = move_uid;
}
}
fixup_match_2 (rtx insn, rtx dst, rtx src, rtx offset)
{
rtx p, dst_death = 0;
- int length, num_calls = 0;
+ int length, num_calls = 0, freq_calls = 0;
/* If SRC dies in INSN, we'd have to move the death note. This is
considered to be very unlikely, so we just skip the optimization
remove_death (REGNO (dst), dst_death);
REG_LIVE_LENGTH (REGNO (dst)) += length;
REG_N_CALLS_CROSSED (REGNO (dst)) += num_calls;
+ REG_FREQ_CALLS_CROSSED (REGNO (dst)) += freq_calls;
}
if (dump_file)
if (CALL_P (p))
{
if (! dst_death)
- num_calls++;
+ {
+ num_calls++;
+ freq_calls += REG_FREQ_FROM_BB (BLOCK_FOR_INSN (p));
+ }
if (REG_N_CALLS_CROSSED (REGNO (src)) == 0)
break;
int pass;
int i;
rtx copy_src, copy_dst;
- basic_block bb;
/* ??? Hack. Regmove doesn't examine the CFG, and gets mightily
confused by non-call exceptions ending blocks. */
if (flag_non_call_exceptions)
return;
+ df_note_add_problem ();
+ df_analyze ();
+
+ regstat_init_n_sets_and_refs ();
+ regstat_compute_ri ();
+
/* Find out where a potential flags register is live, and so that we
can suppress some optimizations in those zones. */
mark_flags_life_zones (discover_flags_reg ());
{
rtx set, p, src, dst;
rtx src_note, dst_note;
- int num_calls = 0;
+ int num_calls = 0, freq_calls = 0;
enum reg_class src_class, dst_class;
int length;
if (CALL_P (p))
{
num_calls++;
+ freq_calls += REG_FREQ_FROM_BB (BLOCK_FOR_INSN (p));
if (REG_N_CALLS_CROSSED (REGNO (dst)) == 0)
break;
dstno = REGNO (dst);
srcno = REGNO (src);
- REG_N_SETS (dstno)++;
- REG_N_SETS (srcno)--;
+ INC_REG_N_SETS (dstno, 1);
+ INC_REG_N_SETS (srcno, -1);
REG_N_CALLS_CROSSED (dstno) += num_calls;
REG_N_CALLS_CROSSED (srcno) -= num_calls;
+ REG_FREQ_CALLS_CROSSED (dstno) += freq_calls;
+ REG_FREQ_CALLS_CROSSED (srcno) -= freq_calls;
REG_LIVE_LENGTH (dstno) += length;
if (REG_LIVE_LENGTH (srcno) >= 0)
alternative approach of copying the source to the destination. */
if (!success && copy_src != NULL_RTX)
copy_src_to_dest (insn, copy_src, copy_dst);
-
}
}
free (reg_set_in_bb);
reg_set_in_bb = NULL;
}
+ regstat_free_n_sets_and_refs ();
+ regstat_free_ri ();
}
/* Returns nonzero if INSN's pattern has matching constraints for any operand.
rtx p;
rtx post_inc = 0, post_inc_set = 0, search_end = 0;
int success = 0;
- int num_calls = 0, s_num_calls = 0;
+ int num_calls = 0, freq_calls = 0, s_num_calls = 0, s_freq_calls = 0;
enum rtx_code code = NOTE;
HOST_WIDE_INT insn_const = 0, newconst = 0;
rtx overlap = 0; /* need to move insn ? */
break;
num_calls++;
+ freq_calls += REG_FREQ_FROM_BB (BLOCK_FOR_INSN (p));
if (src_note)
- s_num_calls++;
-
+ {
+ s_num_calls++;
+ s_freq_calls += REG_FREQ_FROM_BB (BLOCK_FOR_INSN (p));
+ }
}
}
&& try_auto_increment (search_end, post_inc, 0, src, newconst, 1))
post_inc = 0;
validate_change (insn, &XEXP (SET_SRC (set), 1), GEN_INT (insn_const), 0);
- REG_N_SETS (REGNO (src))++;
+ INC_REG_N_SETS (REGNO (src), 1);
REG_LIVE_LENGTH (REGNO (src))++;
}
if (overlap)
p = emit_insn_after_setloc (pat, PREV_INSN (p), INSN_LOCATOR (insn));
delete_insn (insn);
REG_NOTES (p) = notes;
+ df_notes_rescan (p);
}
}
/* Sometimes we'd generate src = const; src += n;
{
rtx note = find_reg_note (insn, REG_EQUAL, NULL_RTX);
rtx q, set2 = NULL_RTX;
- int num_calls2 = 0, s_length2 = 0;
+ int num_calls2 = 0, s_length2 = 0, freq_calls2 = 0;
if (note && CONSTANT_P (XEXP (note, 0)))
{
break;
}
if (CALL_P (p))
- num_calls2++;
+ {
+ num_calls2++;
+ freq_calls2 += REG_FREQ_FROM_BB (BLOCK_FOR_INSN (p));
+ }
}
if (q && set2 && SET_DEST (set2) == src && CONSTANT_P (SET_SRC (set2))
&& validate_change (insn, &SET_SRC (set), XEXP (note, 0), 0))
{
delete_insn (q);
- REG_N_SETS (REGNO (src))--;
+ INC_REG_N_SETS (REGNO (src), -1);
REG_N_CALLS_CROSSED (REGNO (src)) -= num_calls2;
+ REG_FREQ_CALLS_CROSSED (REGNO (src)) -= freq_calls2;
REG_LIVE_LENGTH (REGNO (src)) -= s_length2;
insn_const = 0;
}
REG_NOTES (p) = src_note;
REG_N_CALLS_CROSSED (REGNO (src)) += s_num_calls;
+ REG_FREQ_CALLS_CROSSED (REGNO (src)) += s_freq_calls;
}
- REG_N_SETS (REGNO (src))++;
- REG_N_SETS (REGNO (dst))--;
+ INC_REG_N_SETS (REGNO (src), 1);
+ INC_REG_N_SETS (REGNO (dst), -1);
REG_N_CALLS_CROSSED (REGNO (dst)) -= num_calls;
+ REG_FREQ_CALLS_CROSSED (REGNO (dst)) -= freq_calls;
REG_LIVE_LENGTH (REGNO (src)) += s_length;
if (REG_LIVE_LENGTH (REGNO (dst)) >= 0)
rest_of_handle_regmove (void)
{
regmove_optimize (get_insns (), max_reg_num ());
- cleanup_cfg (CLEANUP_EXPENSIVE | CLEANUP_UPDATE_LIFE);
return 0;
}
-struct tree_opt_pass pass_regmove =
+struct rtl_opt_pass pass_regmove =
{
+ {
+ RTL_PASS,
"regmove", /* name */
gate_handle_regmove, /* gate */
rest_of_handle_regmove, /* execute */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
+ TODO_df_finish | TODO_verify_rtl_sharing |
TODO_dump_func |
- TODO_ggc_collect, /* todo_flags_finish */
- 'N' /* letter */
+ TODO_ggc_collect /* todo_flags_finish */
+ }
};