/* Decompose multiword subregs.
- Copyright (C) 2007 Free Software Foundation, Inc.
+ Copyright (C) 2007, 2008, 2009, 2010 Free Software Foundation, Inc.
Contributed by Richard Henderson <rth@redhat.com>
Ian Lance Taylor <iant@google.com>
#include "basic-block.h"
#include "recog.h"
#include "bitmap.h"
+#include "dce.h"
#include "expr.h"
#include "except.h"
#include "regs.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
unsigned int byte)
{
unsigned int inner_size;
- enum machine_mode innermode;
+ enum machine_mode innermode, partmode;
rtx part;
unsigned int final_offset;
inner_size = GET_MODE_SIZE (innermode) / XVECLEN (op, 0);
part = XVECEXP (op, 0, byte / inner_size);
+ partmode = GET_MODE (part);
+
+ /* VECTOR_CSTs in debug expressions are expanded into CONCATN instead of
+ regular CONST_VECTORs. They have vector or integer modes, depending
+ on the capabilities of the target. Cope with them. */
+ if (partmode == VOIDmode && VECTOR_MODE_P (innermode))
+ partmode = GET_MODE_INNER (innermode);
+ else if (partmode == VOIDmode)
+ {
+ enum mode_class mclass = GET_MODE_CLASS (innermode);
+ partmode = mode_for_size (inner_size * BITS_PER_UNIT, mclass, 0);
+ }
+
final_offset = byte % inner_size;
if (final_offset + GET_MODE_SIZE (outermode) > inner_size)
return NULL_RTX;
- return simplify_gen_subreg (outermode, part, GET_MODE (part), final_offset);
+ return simplify_gen_subreg (outermode, part, partmode, final_offset);
}
/* Wrapper around simplify_gen_subreg which handles CONCATN. */
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;
static bool
resolve_clobber (rtx pat, rtx insn)
{
- rtx reg, note;
+ rtx reg;
enum machine_mode orig_mode;
unsigned int words, i;
int ret;
if (!resolve_reg_p (reg) && !resolve_subreg_p (reg))
return false;
- /* If this clobber has a REG_LIBCALL note, then it is the initial
- clobber added by emit_no_conflict_block. We were able to
- decompose the register, so we no longer need the clobber. */
- note = find_reg_note (insn, REG_LIBCALL, NULL_RTX);
- if (note != NULL_RTX)
- {
- remove_retval_note (XEXP (note, 0));
- delete_insn (insn);
- return true;
- }
-
orig_mode = GET_MODE (reg);
words = GET_MODE_SIZE (orig_mode);
words = (words + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
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. */
}
else /* left or right shift */
{
- if (GET_CODE (XEXP (op, 1)) != CONST_INT
+ 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;
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),
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),
+ shift_count - BITS_PER_WORD,
dest_reg, 1);
}
return;
}
+ if (df)
+ run_word_dce ();
+
/* FIXME: When the dataflow branch is merged, we can change this
code to look for each multi-word pseudo-register and to find each
insn which sets or uses that register. That should be faster
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);
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;
+ rtx pat;
if (!INSN_P (insn))
continue;
- next = NEXT_INSN (insn);
-
pat = PATTERN (insn);
if (GET_CODE (pat) == CLOBBER)
resolve_clobber (pat, insn);
else if (GET_CODE (pat) == USE)
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)
{
- remove_retval_note (insn);
-
recog_memoized (insn);
extract_insn (insn);
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);
}
}
}
unsigned int i;
bitmap b;
- for (i = 0; VEC_iterate (bitmap, reg_copy_graph, i, b); ++i)
+ FOR_EACH_VEC_ELT (bitmap, reg_copy_graph, i, b)
if (b)
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, /* properties_provided */
0, /* properties_destroyed */
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_dump_func |
+ TODO_df_finish | TODO_verify_rtl_sharing |
TODO_ggc_collect |
- TODO_verify_flow, /* todo_flags_finish */
- 'U' /* letter */
+ TODO_verify_flow /* todo_flags_finish */
+ }
};