/* Decompose multiword subregs.
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2008, 2009 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>
Ian Lance Taylor <iant@google.com>
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/>. */
#include "config.h"
#include "system.h"
which it can not be decomposed. */
static bitmap non_decomposable_context;
+/* Bit N in this bitmap is set if regno N is used in a subreg
+ which changes the mode but not the size. This typically happens
+ when the register accessed as a floating-point value; we want to
+ avoid generating accesses to its subwords in integer modes. */
+static bitmap subreg_context;
+
/* Bit N in the bitmap in element M of this array is set if there is a
copy from reg M to reg N. */
static VEC(bitmap,heap) *reg_copy_graph;
{
/* Not a simple move from one location to another. */
NOT_SIMPLE_MOVE,
- /* A simple move from one pseudo-register to another with no
- REG_RETVAL note. */
+ /* A simple move from one pseudo-register to another. */
SIMPLE_PSEUDO_REG_MOVE,
- /* A simple move involving a non-pseudo-register, or from one
- pseudo-register to another with a REG_RETVAL note. */
+ /* A simple move involving a non-pseudo-register. */
SIMPLE_MOVE
};
&& !MODES_TIEABLE_P (GET_MODE (x), GET_MODE (inner)))
{
bitmap_set_bit (non_decomposable_context, regno);
+ bitmap_set_bit (subreg_context, regno);
return -1;
}
}
If this is not a simple copy from one location to another,
then we can not decompose this register. If this is a simple
- copy from one pseudo-register to another, with no REG_RETVAL
- note, and the mode is right, then we mark the register as
- decomposable. Otherwise we don't say anything about this
- register--it could be decomposed, but whether that would be
+ copy from one pseudo-register to another, and the mode is right
+ then we mark the register as decomposable.
+ Otherwise we don't say anything about this register --
+ it could be decomposed, but whether that would be
profitable depends upon how it is used elsewhere.
We only set bits in the bitmap for multi-word
{
/* Return 1 to the caller to indicate that we found a direct
reference to a register which is being decomposed. This can
- happen inside notes. */
- gcc_assert (!insn);
+ happen inside notes, multiword shift or zero-extend
+ instructions. */
return 1;
}
return 0;
}
-/* We are deleting INSN. Move any EH_REGION notes to INSNS. */
+/* This is called via for_each_rtx. Look for SUBREGs which can be
+ decomposed and decomposed REGs that need copying. */
-static void
-move_eh_region_note (rtx insn, rtx insns)
+static int
+adjust_decomposed_uses (rtx *px, void *data ATTRIBUTE_UNUSED)
{
- rtx note, p;
-
- note = find_reg_note (insn, REG_EH_REGION, NULL_RTX);
- if (note == NULL_RTX)
- return;
+ rtx x = *px;
- gcc_assert (CALL_P (insn)
- || (flag_non_call_exceptions && may_trap_p (PATTERN (insn))));
+ if (x == NULL_RTX)
+ return 0;
- for (p = insns; p != NULL_RTX; p = NEXT_INSN (p))
+ if (resolve_subreg_p (x))
{
- if (CALL_P (p)
- || (flag_non_call_exceptions
- && INSN_P (p)
- && may_trap_p (PATTERN (p))))
- REG_NOTES (p) = gen_rtx_EXPR_LIST (REG_EH_REGION, XEXP (note, 0),
- REG_NOTES (p));
- }
-}
-
-/* If there is a REG_LIBCALL note on OLD_START, move it to NEW_START,
- and link the corresponding REG_RETVAL note to NEW_START. */
-
-static void
-move_libcall_note (rtx old_start, rtx new_start)
-{
- rtx note0, note1, end;
-
- note0 = find_reg_note (old_start, REG_LIBCALL, NULL);
- if (note0 == NULL_RTX)
- return;
-
- remove_note (old_start, note0);
- end = XEXP (note0, 0);
- note1 = find_reg_note (end, REG_RETVAL, NULL);
-
- XEXP (note0, 1) = REG_NOTES (new_start);
- REG_NOTES (new_start) = note0;
- XEXP (note1, 0) = new_start;
-}
-
-/* Remove any REG_RETVAL note, the corresponding REG_LIBCALL note, and
- any markers for a no-conflict block. We have decomposed the
- registers so the non-conflict is now obvious. */
-
-static void
-remove_retval_note (rtx insn1)
-{
- rtx note0, insn0, note1, insn;
-
- note1 = find_reg_note (insn1, REG_RETVAL, NULL);
- if (note1 == NULL_RTX)
- return;
-
- insn0 = XEXP (note1, 0);
- note0 = find_reg_note (insn0, REG_LIBCALL, NULL);
+ x = simplify_subreg_concatn (GET_MODE (x), SUBREG_REG (x),
+ SUBREG_BYTE (x));
- remove_note (insn0, note0);
- remove_note (insn1, note1);
+ if (x)
+ *px = x;
+ else
+ x = copy_rtx (*px);
+ }
- for (insn = insn0; insn != insn1; insn = NEXT_INSN (insn))
- {
- while (1)
- {
- rtx note;
+ if (resolve_reg_p (x))
+ *px = copy_rtx (x);
- note = find_reg_note (insn, REG_NO_CONFLICT, NULL);
- if (note == NULL_RTX)
- break;
- remove_note (insn, note);
- }
- }
+ return 0;
}
/* Resolve any decomposed registers which appear in register notes on
{
int old_count = num_validated_changes ();
if (for_each_rtx (&XEXP (note, 0), resolve_subreg_use, NULL))
- {
- remove_note (insn, note);
- remove_retval_note (insn);
- }
+ remove_note (insn, note);
else
if (old_count != num_validated_changes ())
df_notes_rescan (insn);
pnote = ®_NOTES (insn);
while (*pnote != NULL_RTX)
{
- bool delete = false;
+ bool del = false;
note = *pnote;
switch (REG_NOTE_KIND (note))
{
- case REG_NO_CONFLICT:
case REG_DEAD:
case REG_UNUSED:
if (resolve_reg_p (XEXP (note, 0)))
- delete = true;
+ del = true;
break;
default:
break;
}
- if (delete)
+ if (del)
*pnote = XEXP (note, 1);
else
pnote = &XEXP (note, 1);
return (validate_subreg (word_mode, GET_MODE (x), x, UNITS_PER_WORD)
&& HARD_REGNO_MODE_OK (regno, word_mode));
else
- return !bitmap_bit_p (non_decomposable_context, regno);
+ return !bitmap_bit_p (subreg_context, regno);
}
return true;
unsigned int i;
if (REG_P (dest) && !HARD_REGISTER_NUM_P (REGNO (dest)))
- emit_insn (gen_rtx_CLOBBER (VOIDmode, dest));
+ emit_clobber (dest);
for (i = 0; i < words; ++i)
emit_move_insn (simplify_gen_subreg_concatn (word_mode, dest,
insns = get_insns ();
end_sequence ();
- move_eh_region_note (insn, insns);
+ copy_reg_eh_region_note_forward (insn, insns, NULL_RTX);
emit_insn_before (insns, insn);
- move_libcall_note (insn, insns);
- remove_retval_note (insn);
delete_insn (insn);
return insns;
emit_insn_after (x, insn);
}
+ resolve_reg_notes (insn);
+
return true;
}
delete_insn (insn);
return true;
}
+
+ resolve_reg_notes (insn);
+
return false;
}
+/* A VAR_LOCATION can be simplified. */
+
+static void
+resolve_debug (rtx insn)
+{
+ for_each_rtx (&PATTERN (insn), adjust_decomposed_uses, NULL_RTX);
+
+ df_insn_rescan (insn);
+
+ resolve_reg_notes (insn);
+}
+
+/* Checks if INSN is a decomposable multiword-shift or zero-extend and
+ sets the decomposable_context bitmap accordingly. A non-zero value
+ is returned if a decomposable insn has been found. */
+
+static int
+find_decomposable_shift_zext (rtx insn)
+{
+ rtx set;
+ rtx op;
+ rtx op_operand;
+
+ set = single_set (insn);
+ if (!set)
+ return 0;
+
+ op = SET_SRC (set);
+ if (GET_CODE (op) != ASHIFT
+ && GET_CODE (op) != LSHIFTRT
+ && GET_CODE (op) != ZERO_EXTEND)
+ return 0;
+
+ op_operand = XEXP (op, 0);
+ if (!REG_P (SET_DEST (set)) || !REG_P (op_operand)
+ || HARD_REGISTER_NUM_P (REGNO (SET_DEST (set)))
+ || HARD_REGISTER_NUM_P (REGNO (op_operand))
+ || !SCALAR_INT_MODE_P (GET_MODE (op)))
+ return 0;
+
+ if (GET_CODE (op) == ZERO_EXTEND)
+ {
+ if (GET_MODE (op_operand) != word_mode
+ || GET_MODE_BITSIZE (GET_MODE (op)) != 2 * BITS_PER_WORD)
+ return 0;
+ }
+ else /* left or right shift */
+ {
+ if (!CONST_INT_P (XEXP (op, 1))
+ || INTVAL (XEXP (op, 1)) < BITS_PER_WORD
+ || GET_MODE_BITSIZE (GET_MODE (op_operand)) != 2 * BITS_PER_WORD)
+ return 0;
+ }
+
+ bitmap_set_bit (decomposable_context, REGNO (SET_DEST (set)));
+
+ if (GET_CODE (op) != ZERO_EXTEND)
+ bitmap_set_bit (decomposable_context, REGNO (op_operand));
+
+ return 1;
+}
+
+/* Decompose a more than word wide shift (in INSN) of a multiword
+ pseudo or a multiword zero-extend of a wordmode pseudo into a move
+ and 'set to zero' insn. Return a pointer to the new insn when a
+ replacement was done. */
+
+static rtx
+resolve_shift_zext (rtx insn)
+{
+ rtx set;
+ rtx op;
+ rtx op_operand;
+ rtx insns;
+ rtx src_reg, dest_reg, dest_zero;
+ int src_reg_num, dest_reg_num, offset1, offset2, src_offset;
+
+ set = single_set (insn);
+ if (!set)
+ return NULL_RTX;
+
+ op = SET_SRC (set);
+ if (GET_CODE (op) != ASHIFT
+ && GET_CODE (op) != LSHIFTRT
+ && GET_CODE (op) != ZERO_EXTEND)
+ return NULL_RTX;
+
+ op_operand = XEXP (op, 0);
+
+ if (!resolve_reg_p (SET_DEST (set)) && !resolve_reg_p (op_operand))
+ return NULL_RTX;
+
+ /* src_reg_num is the number of the word mode register which we
+ are operating on. For a left shift and a zero_extend on little
+ endian machines this is register 0. */
+ src_reg_num = GET_CODE (op) == LSHIFTRT ? 1 : 0;
+
+ if (WORDS_BIG_ENDIAN
+ && GET_MODE_SIZE (GET_MODE (op_operand)) > UNITS_PER_WORD)
+ src_reg_num = 1 - src_reg_num;
+
+ if (GET_CODE (op) == ZERO_EXTEND)
+ dest_reg_num = WORDS_BIG_ENDIAN ? 1 : 0;
+ else
+ dest_reg_num = 1 - src_reg_num;
+
+ offset1 = UNITS_PER_WORD * dest_reg_num;
+ offset2 = UNITS_PER_WORD * (1 - dest_reg_num);
+ src_offset = UNITS_PER_WORD * src_reg_num;
+
+ if (WORDS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
+ {
+ offset1 += UNITS_PER_WORD - 1;
+ offset2 += UNITS_PER_WORD - 1;
+ src_offset += UNITS_PER_WORD - 1;
+ }
+
+ start_sequence ();
+
+ dest_reg = simplify_gen_subreg_concatn (word_mode, SET_DEST (set),
+ GET_MODE (SET_DEST (set)),
+ offset1);
+ dest_zero = simplify_gen_subreg_concatn (word_mode, SET_DEST (set),
+ GET_MODE (SET_DEST (set)),
+ offset2);
+ src_reg = simplify_gen_subreg_concatn (word_mode, op_operand,
+ GET_MODE (op_operand),
+ src_offset);
+ if (GET_CODE (op) != ZERO_EXTEND)
+ {
+ int shift_count = INTVAL (XEXP (op, 1));
+ if (shift_count > BITS_PER_WORD)
+ src_reg = expand_shift (GET_CODE (op) == ASHIFT ?
+ LSHIFT_EXPR : RSHIFT_EXPR,
+ word_mode, src_reg,
+ build_int_cst (NULL_TREE,
+ shift_count - BITS_PER_WORD),
+ dest_reg, 1);
+ }
+
+ if (dest_reg != src_reg)
+ emit_move_insn (dest_reg, src_reg);
+ emit_move_insn (dest_zero, CONST0_RTX (word_mode));
+ insns = get_insns ();
+
+ end_sequence ();
+
+ emit_insn_before (insns, insn);
+
+ if (dump_file)
+ {
+ rtx in;
+ fprintf (dump_file, "; Replacing insn: %d with insns: ", INSN_UID (insn));
+ for (in = insns; in != insn; in = NEXT_INSN (in))
+ fprintf (dump_file, "%d ", INSN_UID (in));
+ fprintf (dump_file, "\n");
+ }
+
+ delete_insn (insn);
+ return insns;
+}
+
/* Look for registers which are always accessed via word-sized SUBREGs
or via copies. Decompose these registers into several word-sized
pseudo-registers. */
decomposable_context = BITMAP_ALLOC (NULL);
non_decomposable_context = BITMAP_ALLOC (NULL);
+ subreg_context = BITMAP_ALLOC (NULL);
reg_copy_graph = VEC_alloc (bitmap, heap, max);
VEC_safe_grow (bitmap, heap, reg_copy_graph, max);
|| GET_CODE (PATTERN (insn)) == USE)
continue;
+ if (find_decomposable_shift_zext (insn))
+ continue;
+
recog_memoized (insn);
extract_insn (insn);
cmi = NOT_SIMPLE_MOVE;
else
{
- bool retval;
-
- retval = find_reg_note (insn, REG_RETVAL, NULL_RTX) != NULL_RTX;
-
- if (find_pseudo_copy (set) && !retval)
+ if (find_pseudo_copy (set))
cmi = SIMPLE_PSEUDO_REG_MOVE;
- else if (retval
- && REG_P (SET_SRC (set))
- && HARD_REGISTER_P (SET_SRC (set)))
- {
- rtx note;
-
- /* We don't want to decompose an assignment which
- copies the value returned by a libcall to a
- pseudo-register. Doing that will lose the RETVAL
- note with no real gain. */
- cmi = NOT_SIMPLE_MOVE;
-
- /* If we have a RETVAL note, there should be an
- EQUAL note. We don't want to decompose any
- registers which that EQUAL note refers to
- directly. If we do, we will no longer know the
- value of the libcall. */
- note = find_reg_equal_equiv_note (insn);
- if (note != NULL_RTX)
- for_each_rtx (&XEXP (note, 0), find_decomposable_subregs,
- &cmi);
- }
else
cmi = SIMPLE_MOVE;
}
FOR_BB_INSNS (bb, insn)
{
- rtx next, pat;
- bool changed;
+ rtx pat;
if (!INSN_P (insn))
continue;
- next = NEXT_INSN (insn);
- changed = false;
-
pat = PATTERN (insn);
if (GET_CODE (pat) == CLOBBER)
- {
- if (resolve_clobber (pat, insn))
- changed = true;
- }
+ resolve_clobber (pat, insn);
else if (GET_CODE (pat) == USE)
- {
- if (resolve_use (pat, insn))
- changed = true;
- }
+ resolve_use (pat, insn);
+ else if (DEBUG_INSN_P (insn))
+ resolve_debug (insn);
else
{
rtx set;
basic block and still produce the correct control
flow graph for it. */
gcc_assert (!cfi
- || (flag_non_call_exceptions
+ || (cfun->can_throw_non_call_exceptions
&& can_throw_internal (insn)));
insn = resolve_simple_move (set, insn);
if (insn != orig_insn)
{
- changed = true;
-
- remove_retval_note (insn);
-
recog_memoized (insn);
extract_insn (insn);
SET_BIT (sub_blocks, bb->index);
}
}
+ else
+ {
+ rtx decomposed_shift;
+
+ decomposed_shift = resolve_shift_zext (insn);
+ if (decomposed_shift != NULL_RTX)
+ {
+ insn = decomposed_shift;
+ recog_memoized (insn);
+ extract_insn (insn);
+ }
+ }
for (i = recog_data.n_operands - 1; i >= 0; --i)
for_each_rtx (recog_data.operand_loc[i],
int dup_num = recog_data.dup_num[i];
rtx *px = recog_data.operand_loc[dup_num];
- validate_change (insn, pl, *px, 1);
+ validate_unshare_change (insn, pl, *px, 1);
}
i = apply_change_group ();
gcc_assert (i);
-
- remove_retval_note (insn);
-
- changed = true;
}
}
}
BITMAP_FREE (b);
}
- VEC_free (bitmap, heap, reg_copy_graph);
+ VEC_free (bitmap, heap, reg_copy_graph);
BITMAP_FREE (decomposable_context);
BITMAP_FREE (non_decomposable_context);
+ BITMAP_FREE (subreg_context);
}
\f
/* Gate function for lower subreg pass. */
return 0;
}
-struct tree_opt_pass pass_lower_subreg =
+struct rtl_opt_pass pass_lower_subreg =
{
- "subreg", /* name */
+ {
+ RTL_PASS,
+ "subreg1", /* name */
gate_handle_lower_subreg, /* gate */
rest_of_handle_lower_subreg, /* execute */
NULL, /* sub */
0, /* todo_flags_start */
TODO_dump_func |
TODO_ggc_collect |
- TODO_verify_flow, /* todo_flags_finish */
- 'u' /* letter */
+ TODO_verify_flow /* todo_flags_finish */
+ }
};
-struct tree_opt_pass pass_lower_subreg2 =
+struct rtl_opt_pass pass_lower_subreg2 =
{
+ {
+ RTL_PASS,
"subreg2", /* name */
gate_handle_lower_subreg, /* gate */
rest_of_handle_lower_subreg2, /* execute */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_df_finish |
+ TODO_df_finish | TODO_verify_rtl_sharing |
TODO_dump_func |
TODO_ggc_collect |
- TODO_verify_flow, /* todo_flags_finish */
- 'U' /* letter */
+ TODO_verify_flow /* todo_flags_finish */
+ }
};