This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [patch] Store motion rewrite
Hello,
> > ! /* Do not consider MEMs that mention stack pointer; in the following
> > ! we rely on that constant functions do not read memory, which of course
> > ! does not include their arguments if passed on stack. */
> > ! if (reg_mentioned_p (stack_pointer_rtx, dest))
> > return;
>
> Really this needs to be any value whose *base* is the stack
> pointer. Consider passing a large structure on the stack,
> large enough that we overflow the architecture's immediate
> offset from the sp, so we've generated
>
> (set (reg temp) (plus (reg sp) (const_int high_part))
> (set (mem (plus (reg temp) (const_int low_part))) (foo))
>
> For a target like SH that has very limited immediate offsets,
> this might be a real problem. You should be able to test this
> using a function of about 20 arguments, I think.
here is the new version. I was unable to find a simple way how to
check for this -- building du chains or something like that just for
this purpose is overkill. So I have just altered it the way we expect
even constant functions to read any mem whose base contains any
register.
Zdenek
Index: gcse.c
===================================================================
RCS file: /cvs/gcc/gcc/gcc/gcse.c,v
retrieving revision 1.241
diff -c -3 -p -r1.241 gcse.c
*** gcse.c 31 Mar 2003 06:28:55 -0000 1.241
--- gcse.c 31 Mar 2003 23:05:42 -0000
*************** struct ls_expr
*** 470,475 ****
--- 470,476 ----
{
struct expr * expr; /* Gcse expression reference for LM. */
rtx pattern; /* Pattern of this mem. */
+ rtx pattern_regs; /* List of registers mentioned by the mem. */
rtx loads; /* INSN list of loads seen. */
rtx stores; /* INSN list of stores seen. */
struct ls_expr * next; /* Next in the list. */
*************** static void compute_ld_motion_mems PARAM
*** 687,700 ****
static void trim_ld_motion_mems PARAMS ((void));
static void update_ld_motion_stores PARAMS ((struct expr *));
static void reg_set_info PARAMS ((rtx, rtx, void *));
! static int store_ops_ok PARAMS ((rtx, basic_block));
! static void find_moveable_store PARAMS ((rtx));
static int compute_store_table PARAMS ((void));
! static int load_kills_store PARAMS ((rtx, rtx));
! static int find_loads PARAMS ((rtx, rtx));
! static int store_killed_in_insn PARAMS ((rtx, rtx));
! static int store_killed_after PARAMS ((rtx, rtx, basic_block));
! static int store_killed_before PARAMS ((rtx, rtx, basic_block));
static void build_store_vectors PARAMS ((void));
static void insert_insn_start_bb PARAMS ((rtx, basic_block));
static int insert_store PARAMS ((struct ls_expr *, edge));
--- 688,705 ----
static void trim_ld_motion_mems PARAMS ((void));
static void update_ld_motion_stores PARAMS ((struct expr *));
static void reg_set_info PARAMS ((rtx, rtx, void *));
! static bool store_ops_ok PARAMS ((rtx, int *));
! static rtx extract_mentioned_regs PARAMS ((rtx));
! static rtx extract_mentioned_regs_helper PARAMS ((rtx, rtx));
! static void find_moveable_store PARAMS ((rtx, int *, int *));
static int compute_store_table PARAMS ((void));
! static bool load_kills_store PARAMS ((rtx, rtx));
! static bool find_loads PARAMS ((rtx, rtx));
! static bool store_killed_in_insn PARAMS ((rtx, rtx, rtx));
! static bool store_killed_after PARAMS ((rtx, rtx, rtx, basic_block,
! int *, rtx *));
! static bool store_killed_before PARAMS ((rtx, rtx, rtx, basic_block,
! int *));
static void build_store_vectors PARAMS ((void));
static void insert_insn_start_bb PARAMS ((rtx, basic_block));
static int insert_store PARAMS ((struct ls_expr *, edge));
*************** gcse_main (f, file)
*** 910,918 ****
end_alias_analysis ();
allocate_reg_info (max_reg_num (), FALSE, FALSE);
! /* Store motion disabled until it is fixed. */
! if (0 && !optimize_size && flag_gcse_sm)
store_motion ();
/* Record where pseudo-registers are set. */
return run_jump_opt_after_gcse;
}
--- 915,923 ----
end_alias_analysis ();
allocate_reg_info (max_reg_num (), FALSE, FALSE);
! if (!optimize_size && flag_gcse_sm)
store_motion ();
+
/* Record where pseudo-registers are set. */
return run_jump_opt_after_gcse;
}
*************** mems_conflict_for_gcse_p (dest, setter,
*** 1464,1470 ****
/* If we are setting a MEM in our list of specially recognized MEMs,
don't mark as killed this time. */
! if (dest == gcse_mem_operand && pre_ldst_mems != NULL)
{
if (!find_rtx_in_ldst (dest))
gcse_mems_conflict_p = 1;
--- 1469,1475 ----
/* If we are setting a MEM in our list of specially recognized MEMs,
don't mark as killed this time. */
! if (expr_equiv_p (dest, gcse_mem_operand) && pre_ldst_mems != NULL)
{
if (!find_rtx_in_ldst (dest))
gcse_mems_conflict_p = 1;
*************** pre_insert_copy_insn (expr, insn)
*** 5438,5444 ****
if (!set)
abort ();
! new_insn = emit_insn_after (gen_move_insn (reg, SET_DEST (set)), insn);
/* Keep register set table up to date. */
record_one_set (regno, new_insn);
--- 5443,5449 ----
if (!set)
abort ();
! new_insn = emit_insn_after (gen_move_insn (reg, copy_rtx (SET_DEST (set))), insn);
/* Keep register set table up to date. */
record_one_set (regno, new_insn);
*************** ldst_entry (x)
*** 6515,6520 ****
--- 6520,6526 ----
ptr->next = pre_ldst_mems;
ptr->expr = NULL;
ptr->pattern = x;
+ ptr->pattern_regs = NULL_RTX;
ptr->loads = NULL_RTX;
ptr->stores = NULL_RTX;
ptr->reaching_reg = NULL_RTX;
*************** simple_mem (x)
*** 6657,6669 ****
if (GET_MODE (x) == BLKmode)
return 0;
! if (flag_float_store && FLOAT_MODE_P (GET_MODE (x)))
return 0;
! if (!rtx_varies_p (XEXP (x, 0), 0))
! return 1;
! return 0;
}
/* Make sure there isn't a buried reference in this pattern anywhere.
--- 6663,6685 ----
if (GET_MODE (x) == BLKmode)
return 0;
! /* If we are handling exceptions, we must be careful with memory references
! that may trap. If we are not, the behavior is undefined, so we may just
! continue. */
! if (flag_non_call_exceptions && may_trap_p (x))
return 0;
! if (side_effects_p (x))
! return 0;
! /* Do not consider function arguments passed on stack. */
! if (reg_mentioned_p (stack_pointer_rtx, x))
! return 0;
!
! if (flag_float_store && FLOAT_MODE_P (GET_MODE (x)))
! return 0;
!
! return 1;
}
/* Make sure there isn't a buried reference in this pattern anywhere.
*************** update_ld_motion_stores (expr)
*** 6876,6882 ****
fprintf (gcse_file, "\n");
}
! copy = gen_move_insn ( reg, SET_SRC (pat));
new = emit_insn_before (copy, insn);
record_one_set (REGNO (reg), new);
SET_SRC (pat) = reg;
--- 6892,6898 ----
fprintf (gcse_file, "\n");
}
! copy = gen_move_insn ( reg, copy_rtx (SET_SRC (pat)));
new = emit_insn_before (copy, insn);
record_one_set (REGNO (reg), new);
SET_SRC (pat) = reg;
*************** update_ld_motion_stores (expr)
*** 6890,6898 ****
/* Store motion code. */
/* This is used to communicate the target bitvector we want to use in the
reg_set_info routine when called via the note_stores mechanism. */
! static sbitmap * regvec;
/* Used in computing the reverse edge graph bit vectors. */
static sbitmap * st_antloc;
--- 6906,6921 ----
/* Store motion code. */
+ #define ANTIC_STORE_LIST(x) ((x)->loads)
+ #define AVAIL_STORE_LIST(x) ((x)->stores)
+ #define LAST_AVAIL_CHECK_FAILURE(x) ((x)->reaching_reg)
+
/* This is used to communicate the target bitvector we want to use in the
reg_set_info routine when called via the note_stores mechanism. */
! static int * regvec;
!
! /* And current insn, for the same routine. */
! static rtx compute_store_table_current_insn;
/* Used in computing the reverse edge graph bit vectors. */
static sbitmap * st_antloc;
*************** reg_set_info (dest, setter, data)
*** 6911,6926 ****
dest = SUBREG_REG (dest);
if (GET_CODE (dest) == REG)
! SET_BIT (*regvec, REGNO (dest));
}
! /* Return nonzero if the register operands of expression X are killed
! anywhere in basic block BB. */
! static int
! store_ops_ok (x, bb)
rtx x;
! basic_block bb;
{
int i;
enum rtx_code code;
--- 6934,6976 ----
dest = SUBREG_REG (dest);
if (GET_CODE (dest) == REG)
! regvec[REGNO (dest)] = INSN_UID (compute_store_table_current_insn);
}
! /* Return zero if some of the registers in list X are killed
! due to set of registers in bitmap REGS_SET. */
!
! static bool
! store_ops_ok (x, regs_set)
! rtx x;
! int *regs_set;
! {
! rtx reg;
!
! for (; x; x = XEXP (x, 1))
! {
! reg = XEXP (x, 0);
! if (regs_set[REGNO(reg)])
! return false;
! }
! return true;
! }
!
! /* Returns a list of registers mentioned in X. */
! static rtx
! extract_mentioned_regs (x)
rtx x;
! {
! return extract_mentioned_regs_helper (x, NULL_RTX);
! }
!
! /* Helper for extract_mentioned_regs; ACCUM is used to accumulate used
! registers. */
! static rtx
! extract_mentioned_regs_helper (x, accum)
! rtx x;
! rtx accum;
{
int i;
enum rtx_code code;
*************** store_ops_ok (x, bb)
*** 6930,6944 ****
repeat:
if (x == 0)
! return 1;
code = GET_CODE (x);
switch (code)
{
case REG:
! /* If a reg has changed after us in this
! block, the operand has been killed. */
! return TEST_BIT (reg_set_in_block[bb->index], REGNO (x));
case MEM:
x = XEXP (x, 0);
--- 6980,6992 ----
repeat:
if (x == 0)
! return accum;
code = GET_CODE (x);
switch (code)
{
case REG:
! return alloc_EXPR_LIST (0, x, accum);
case MEM:
x = XEXP (x, 0);
*************** store_ops_ok (x, bb)
*** 6948,6954 ****
case PRE_INC:
case POST_DEC:
case POST_INC:
! return 0;
case PC:
case CC0: /*FIXME*/
--- 6996,7003 ----
case PRE_INC:
case POST_DEC:
case POST_INC:
! /* We do not run this function with arguments having side effects. */
! abort ();
case PC:
case CC0: /*FIXME*/
*************** store_ops_ok (x, bb)
*** 6960,6966 ****
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
! return 1;
default:
break;
--- 7009,7015 ----
case LABEL_REF:
case ADDR_VEC:
case ADDR_DIFF_VEC:
! return accum;
default:
break;
*************** store_ops_ok (x, bb)
*** 6976,7038 ****
rtx tem = XEXP (x, i);
/* If we are about to do the last recursive call
! needed at this level, change it into iteration.
! This function is called enough to be worth it. */
if (i == 0)
{
x = tem;
goto repeat;
}
! if (! store_ops_ok (tem, bb))
! return 0;
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
! {
! if (! store_ops_ok (XVECEXP (x, i, j), bb))
! return 0;
! }
}
}
! return 1;
}
! /* Determine whether insn is MEM store pattern that we will consider moving. */
static void
! find_moveable_store (insn)
rtx insn;
{
struct ls_expr * ptr;
! rtx dest = PATTERN (insn);
! if (GET_CODE (dest) != SET
! || GET_CODE (SET_SRC (dest)) == ASM_OPERANDS)
return;
! dest = SET_DEST (dest);
if (GET_CODE (dest) != MEM || MEM_VOLATILE_P (dest)
|| GET_MODE (dest) == BLKmode)
return;
! if (GET_CODE (XEXP (dest, 0)) != SYMBOL_REF)
! return;
! if (rtx_varies_p (XEXP (dest, 0), 0))
return;
ptr = ldst_entry (dest);
! ptr->stores = alloc_INSN_LIST (insn, ptr->stores);
! }
! /* Perform store motion. Much like gcse, except we move expressions the
! other way by looking at the flowgraph in reverse. */
static int
compute_store_table ()
--- 7025,7174 ----
rtx tem = XEXP (x, i);
/* If we are about to do the last recursive call
! needed at this level, change it into iteration. */
if (i == 0)
{
x = tem;
goto repeat;
}
! accum = extract_mentioned_regs_helper (tem, accum);
}
else if (fmt[i] == 'E')
{
int j;
for (j = 0; j < XVECLEN (x, i); j++)
! accum = extract_mentioned_regs_helper (XVECEXP (x, i, j), accum);
}
}
! return accum;
}
! /* Determine whether INSN is MEM store pattern that we will consider moving.
! REGS_SET_BEFORE is bitmap of registers set before (and including) the
! current insn, REGS_SET_AFTER is bitmap of registers set after (and
! including) the insn in this basic block. We must be passing through BB from
! head to end, as we are using this fact to speed things up.
!
! The results are stored this way:
!
! -- the first anticipatable expression is added into ANTIC_STORE_LIST
! -- if the processed expression is not anticipatable, NULL_RTX is added
! there instead, so that we can use it as indicator that no further
! expression of this type may be anticipatable
! -- if the expression is available, it is added as head of AVAIL_STORE_LIST;
! consequently, all of them but this head are dead and may be deleted.
! -- if the expression is not available, the insn due to that it fails to be
! available is stored in reaching_reg.
!
! The things are complicated a bit by fact that there already may be stores
! to the same MEM from other blocks; also caller must take care of the
! neccessary cleanup of the temporary markers after end of the basic block.
! */
static void
! find_moveable_store (insn, regs_set_before, regs_set_after)
rtx insn;
+ int *regs_set_before;
+ int *regs_set_after;
{
struct ls_expr * ptr;
! rtx dest, set, tmp;
! int check_anticipatable, check_available;
! basic_block bb = BLOCK_FOR_INSN (insn);
! set = single_set (insn);
! if (!set)
return;
! dest = SET_DEST (set);
if (GET_CODE (dest) != MEM || MEM_VOLATILE_P (dest)
|| GET_MODE (dest) == BLKmode)
return;
! if (side_effects_p (dest))
! return;
! /* If we are handling exceptions, we must be careful with memory references
! that may trap. If we are not, the behavior is undefined, so we may just
! continue. */
! if (flag_exceptions && may_trap_p (dest))
! return;
!
! /* Do not consider MEMs that mention stack pointer; in the following
! we rely on that constant functions do not read memory, which of course
! does not include their arguments if passed on stack.
!
! Note that this is not quite correct -- we may use other registers
! to address stack. See store_killed_in_insn for handling of this
! case. */
! if (reg_mentioned_p (stack_pointer_rtx, dest))
return;
ptr = ldst_entry (dest);
! if (!ptr->pattern_regs)
! ptr->pattern_regs = extract_mentioned_regs (dest);
! /* Do not check for anticipatability if we either found one anticipatable
! store already, or tested for one and found out that it was killed. */
! check_anticipatable = 0;
! if (!ANTIC_STORE_LIST (ptr))
! check_anticipatable = 1;
! else
! {
! tmp = XEXP (ANTIC_STORE_LIST (ptr), 0);
! if (tmp != NULL_RTX
! && BLOCK_FOR_INSN (tmp) != bb)
! check_anticipatable = 1;
! }
! if (check_anticipatable)
! {
! if (store_killed_before (dest, ptr->pattern_regs, insn, bb, regs_set_before))
! tmp = NULL_RTX;
! else
! tmp = insn;
! ANTIC_STORE_LIST (ptr) = alloc_INSN_LIST (tmp,
! ANTIC_STORE_LIST (ptr));
! }
!
! /* It is not neccessary to check whether store is available if we did
! it successfully before; if we failed before, do not bother to check
! until we reach the insn that caused us to fail. */
! check_available = 0;
! if (!AVAIL_STORE_LIST (ptr))
! check_available = 1;
! else
! {
! tmp = XEXP (AVAIL_STORE_LIST (ptr), 0);
! if (BLOCK_FOR_INSN (tmp) != bb)
! check_available = 1;
! }
! if (check_available)
! {
! /* Check that we have already reached the insn at that the check
! failed last time. */
! if (LAST_AVAIL_CHECK_FAILURE (ptr))
! {
! for (tmp = bb->end;
! tmp != insn && tmp != LAST_AVAIL_CHECK_FAILURE (ptr);
! tmp = PREV_INSN (tmp))
! continue;
! if (tmp == insn)
! check_available = 0;
! }
! else
! check_available = store_killed_after (dest, ptr->pattern_regs, insn,
! bb, regs_set_after,
! &LAST_AVAIL_CHECK_FAILURE (ptr));
! }
! if (!check_available)
! AVAIL_STORE_LIST (ptr) = alloc_INSN_LIST (insn, AVAIL_STORE_LIST (ptr));
! }
!
! /* Find available and anticipatable stores. */
static int
compute_store_table ()
*************** compute_store_table ()
*** 7040,7046 ****
int ret;
basic_block bb;
unsigned regno;
! rtx insn, pat;
max_gcse_regno = max_reg_num ();
--- 7176,7184 ----
int ret;
basic_block bb;
unsigned regno;
! rtx insn, pat, tmp;
! int *last_set_in, *already_set;
! struct ls_expr * ptr, **prev_next_ptr_ptr;
max_gcse_regno = max_reg_num ();
*************** compute_store_table ()
*** 7048,7063 ****
max_gcse_regno);
sbitmap_vector_zero (reg_set_in_block, last_basic_block);
pre_ldst_mems = 0;
/* Find all the stores we care about. */
FOR_EACH_BB (bb)
{
! regvec = & (reg_set_in_block[bb->index]);
! for (insn = bb->end;
! insn && insn != PREV_INSN (bb->end);
! insn = PREV_INSN (insn))
{
- /* Ignore anything that is not a normal insn. */
if (! INSN_P (insn))
continue;
--- 7186,7205 ----
max_gcse_regno);
sbitmap_vector_zero (reg_set_in_block, last_basic_block);
pre_ldst_mems = 0;
+ last_set_in = xmalloc (sizeof (int) * max_gcse_regno);
+ already_set = xmalloc (sizeof (int) * max_gcse_regno);
/* Find all the stores we care about. */
FOR_EACH_BB (bb)
{
! /* First compute the registers set in this block. */
! memset (last_set_in, 0, sizeof (int) * max_gcse_regno);
! regvec = last_set_in;
!
! for (insn = bb->head;
! insn != NEXT_INSN (bb->end);
! insn = NEXT_INSN (insn))
{
if (! INSN_P (insn))
continue;
*************** compute_store_table ()
*** 7073,7125 ****
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (clobbers_all
|| TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
! SET_BIT (reg_set_in_block[bb->index], regno);
}
pat = PATTERN (insn);
note_stores (pat, reg_set_info, NULL);
/* Now that we've marked regs, look for stores. */
! if (GET_CODE (pat) == SET)
! find_moveable_store (insn);
}
}
ret = enumerate_ldsts ();
if (gcse_file)
{
! fprintf (gcse_file, "Store Motion Expressions.\n");
print_ldst_list (gcse_file);
}
return ret;
}
/* Check to see if the load X is aliased with STORE_PATTERN. */
! static int
load_kills_store (x, store_pattern)
rtx x, store_pattern;
{
if (true_dependence (x, GET_MODE (x), store_pattern, rtx_addr_varies_p))
! return 1;
! return 0;
}
/* Go through the entire insn X, looking for any loads which might alias
! STORE_PATTERN. Return 1 if found. */
! static int
find_loads (x, store_pattern)
rtx x, store_pattern;
{
const char * fmt;
int i, j;
! int ret = 0;
if (!x)
! return 0;
if (GET_CODE (x) == SET)
x = SET_SRC (x);
--- 7215,7332 ----
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (clobbers_all
|| TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
! last_set_in[regno] = INSN_UID (insn);
! }
!
! pat = PATTERN (insn);
! compute_store_table_current_insn = insn;
! note_stores (pat, reg_set_info, NULL);
! }
!
! /* Record the set registers. */
! for (regno = 0; regno < max_gcse_regno; regno++)
! if (last_set_in[regno])
! SET_BIT (reg_set_in_block[bb->index], regno);
!
! /* Now find the stores. */
! memset (already_set, 0, sizeof (int) * max_gcse_regno);
! regvec = already_set;
! for (insn = bb->head;
! insn != NEXT_INSN (bb->end);
! insn = NEXT_INSN (insn))
! {
! if (! INSN_P (insn))
! continue;
!
! if (GET_CODE (insn) == CALL_INSN)
! {
! bool clobbers_all = false;
! #ifdef NON_SAVING_SETJMP
! if (NON_SAVING_SETJMP
! && find_reg_note (insn, REG_SETJMP, NULL_RTX))
! clobbers_all = true;
! #endif
!
! for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
! if (clobbers_all
! || TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
! already_set[regno] = 1;
}
pat = PATTERN (insn);
note_stores (pat, reg_set_info, NULL);
/* Now that we've marked regs, look for stores. */
! find_moveable_store (insn, already_set, last_set_in);
!
! /* Unmark regs that are no longer set. */
! for (regno = 0; regno < max_gcse_regno; regno++)
! if (last_set_in[regno] == INSN_UID (insn))
! last_set_in[regno] = 0;
! }
!
! /* Clear temporary marks. */
! for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
! {
! LAST_AVAIL_CHECK_FAILURE(ptr) = NULL_RTX;
! if (ANTIC_STORE_LIST (ptr)
! && (tmp = XEXP (ANTIC_STORE_LIST (ptr), 0)) == NULL_RTX)
! ANTIC_STORE_LIST (ptr) = XEXP (ANTIC_STORE_LIST (ptr), 1);
! }
! }
!
! /* Remove the stores that are not available anywhere, as there will
! be no opportunity to optimize them. */
! for (ptr = pre_ldst_mems, prev_next_ptr_ptr = &pre_ldst_mems;
! ptr != NULL;
! ptr = *prev_next_ptr_ptr)
! {
! if (!AVAIL_STORE_LIST (ptr))
! {
! *prev_next_ptr_ptr = ptr->next;
! free_ldst_entry (ptr);
}
+ else
+ prev_next_ptr_ptr = &ptr->next;
}
ret = enumerate_ldsts ();
if (gcse_file)
{
! fprintf (gcse_file, "ST_avail and ST_antic (shown under loads..)\n");
print_ldst_list (gcse_file);
}
+ free (last_set_in);
+ free (already_set);
return ret;
}
/* Check to see if the load X is aliased with STORE_PATTERN. */
! static bool
load_kills_store (x, store_pattern)
rtx x, store_pattern;
{
if (true_dependence (x, GET_MODE (x), store_pattern, rtx_addr_varies_p))
! return true;
! return false;
}
/* Go through the entire insn X, looking for any loads which might alias
! STORE_PATTERN. Return true if found. */
! static bool
find_loads (x, store_pattern)
rtx x, store_pattern;
{
const char * fmt;
int i, j;
! int ret = false;
if (!x)
! return false;
if (GET_CODE (x) == SET)
x = SET_SRC (x);
*************** find_loads (x, store_pattern)
*** 7127,7133 ****
if (GET_CODE (x) == MEM)
{
if (load_kills_store (x, store_pattern))
! return 1;
}
/* Recursively process the insn. */
--- 7334,7340 ----
if (GET_CODE (x) == MEM)
{
if (load_kills_store (x, store_pattern))
! return true;
}
/* Recursively process the insn. */
*************** find_loads (x, store_pattern)
*** 7145,7164 ****
}
/* Check if INSN kills the store pattern X (is aliased with it).
! Return 1 if it it does. */
! static int
! store_killed_in_insn (x, insn)
! rtx x, insn;
{
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
! return 0;
if (GET_CODE (insn) == CALL_INSN)
{
/* A normal or pure call might read from pattern,
but a const call will not. */
! return ! CONST_OR_PURE_CALL_P (insn) || pure_call_p (insn);
}
if (GET_CODE (PATTERN (insn)) == SET)
--- 7352,7380 ----
}
/* Check if INSN kills the store pattern X (is aliased with it).
! Return true if it it does. */
! static bool
! store_killed_in_insn (x, x_regs, insn)
! rtx x, x_regs, insn;
{
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
! return false;
if (GET_CODE (insn) == CALL_INSN)
{
/* A normal or pure call might read from pattern,
but a const call will not. */
! if (! CONST_OR_PURE_CALL_P (insn) || pure_call_p (insn))
! return true;
!
! /* But even a const call reads its parameters. It is not trivial
! check that base of the mem is not related to stack pointer,
! so unless it contains no registers, just assume it may. */
! if (x_regs)
! return true;
!
! return false;
}
if (GET_CODE (PATTERN (insn)) == SET)
*************** store_killed_in_insn (x, insn)
*** 7168,7247 ****
if (GET_CODE (SET_DEST (pat)) == MEM && !expr_equiv_p (SET_DEST (pat), x))
/* pretend its a load and check for aliasing. */
if (find_loads (SET_DEST (pat), x))
! return 1;
return find_loads (SET_SRC (pat), x);
}
else
return find_loads (PATTERN (insn), x);
}
! /* Returns 1 if the expression X is loaded or clobbered on or after INSN
! within basic block BB. */
! static int
! store_killed_after (x, insn, bb)
! rtx x, insn;
basic_block bb;
{
! rtx last = bb->end;
! if (insn == last)
! return 0;
!
! /* Check if the register operands of the store are OK in this block.
! Note that if registers are changed ANYWHERE in the block, we'll
! decide we can't move it, regardless of whether it changed above
! or below the store. This could be improved by checking the register
! operands while looking for aliasing in each insn. */
! if (!store_ops_ok (XEXP (x, 0), bb))
! return 1;
!
! for ( ; insn && insn != NEXT_INSN (last); insn = NEXT_INSN (insn))
! if (store_killed_in_insn (x, insn))
! return 1;
! return 0;
}
!
! /* Returns 1 if the expression X is loaded or clobbered on or before INSN
! within basic block BB. */
! static int
! store_killed_before (x, insn, bb)
! rtx x, insn;
basic_block bb;
{
rtx first = bb->head;
! if (insn == first)
! return store_killed_in_insn (x, insn);
!
! /* Check if the register operands of the store are OK in this block.
! Note that if registers are changed ANYWHERE in the block, we'll
! decide we can't move it, regardless of whether it changed above
! or below the store. This could be improved by checking the register
! operands while looking for aliasing in each insn. */
! if (!store_ops_ok (XEXP (x, 0), bb))
! return 1;
! for ( ; insn && insn != PREV_INSN (first); insn = PREV_INSN (insn))
! if (store_killed_in_insn (x, insn))
! return 1;
! return 0;
}
!
! #define ANTIC_STORE_LIST(x) ((x)->loads)
! #define AVAIL_STORE_LIST(x) ((x)->stores)
!
! /* Given the table of available store insns at the end of blocks,
! determine which ones are not killed by aliasing, and generate
! the appropriate vectors for gen and killed. */
static void
build_store_vectors ()
{
! basic_block bb, b;
rtx insn, st;
struct ls_expr * ptr;
/* Build the gen_vector. This is any store in the table which is not killed
by aliasing later in its block. */
--- 7384,7461 ----
if (GET_CODE (SET_DEST (pat)) == MEM && !expr_equiv_p (SET_DEST (pat), x))
/* pretend its a load and check for aliasing. */
if (find_loads (SET_DEST (pat), x))
! return true;
return find_loads (SET_SRC (pat), x);
}
else
return find_loads (PATTERN (insn), x);
}
! /* Returns true if the expression X is loaded or clobbered on or after INSN
! within basic block BB. REGS_SET_AFTER is bitmap of registers set in
! or after the insn. X_REGS is list of registers mentioned in X. If the store
! is killed, return the last insn in that it occurs in FAIL_INSN. */
! static bool
! store_killed_after (x, x_regs, insn, bb, regs_set_after, fail_insn)
! rtx x, x_regs, insn;
basic_block bb;
+ int *regs_set_after;
+ rtx *fail_insn;
{
! rtx last = bb->end, act;
! if (!store_ops_ok (x_regs, regs_set_after))
! {
! /* We do not know where it will happen. */
! if (fail_insn)
! *fail_insn = NULL_RTX;
! return true;
! }
!
! /* Scan from the end, so that fail_insn is determined correctly. */
! for (act = last; act != PREV_INSN (insn); act = PREV_INSN (act))
! if (store_killed_in_insn (x, x_regs, act))
! {
! if (fail_insn)
! *fail_insn = act;
! return true;
! }
! return false;
}
!
! /* Returns true if the expression X is loaded or clobbered on or before INSN
! within basic block BB. X_REGS is list of registers mentioned in X.
! REGS_SET_BEFORE is bitmap of registers set before or in this insn. */
! static bool
! store_killed_before (x, x_regs, insn, bb, regs_set_before)
! rtx x, x_regs, insn;
basic_block bb;
+ int *regs_set_before;
{
rtx first = bb->head;
! if (!store_ops_ok (x_regs, regs_set_before))
! return true;
! for ( ; insn != PREV_INSN (first); insn = PREV_INSN (insn))
! if (store_killed_in_insn (x, x_regs, insn))
! return true;
! return false;
}
!
! /* Fill in available, anticipatable, transparent and kill vectors in
! STORE_DATA, based on lists of available and anticipatable stores. */
static void
build_store_vectors ()
{
! basic_block bb;
! int *regs_set_in_block;
rtx insn, st;
struct ls_expr * ptr;
+ unsigned regno;
/* Build the gen_vector. This is any store in the table which is not killed
by aliasing later in its block. */
*************** build_store_vectors ()
*** 7253,7307 ****
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
{
! /* Put all the stores into either the antic list, or the avail list,
! or both. */
! rtx store_list = ptr->stores;
! ptr->stores = NULL_RTX;
!
! for (st = store_list; st != NULL; st = XEXP (st, 1))
{
insn = XEXP (st, 0);
bb = BLOCK_FOR_INSN (insn);
! if (!store_killed_after (ptr->pattern, insn, bb))
! {
! /* If we've already seen an available expression in this block,
! we can delete the one we saw already (It occurs earlier in
! the block), and replace it with this one). We'll copy the
! old SRC expression to an unused register in case there
! are any side effects. */
! if (TEST_BIT (ae_gen[bb->index], ptr->index))
! {
! /* Find previous store. */
! rtx st;
! for (st = AVAIL_STORE_LIST (ptr); st ; st = XEXP (st, 1))
! if (BLOCK_FOR_INSN (XEXP (st, 0)) == bb)
! break;
! if (st)
! {
! rtx r = gen_reg_rtx (GET_MODE (ptr->pattern));
! if (gcse_file)
! fprintf (gcse_file, "Removing redundant store:\n");
! replace_store_insn (r, XEXP (st, 0), bb);
! XEXP (st, 0) = insn;
! continue;
! }
! }
! SET_BIT (ae_gen[bb->index], ptr->index);
! AVAIL_STORE_LIST (ptr) = alloc_INSN_LIST (insn,
! AVAIL_STORE_LIST (ptr));
! }
!
! if (!store_killed_before (ptr->pattern, insn, bb))
{
! SET_BIT (st_antloc[BLOCK_NUM (insn)], ptr->index);
! ANTIC_STORE_LIST (ptr) = alloc_INSN_LIST (insn,
! ANTIC_STORE_LIST (ptr));
}
}
! /* Free the original list of store insns. */
! free_INSN_LIST_list (&store_list);
}
ae_kill = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
--- 7467,7498 ----
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
{
! for (st = AVAIL_STORE_LIST (ptr); st != NULL; st = XEXP (st, 1))
{
insn = XEXP (st, 0);
bb = BLOCK_FOR_INSN (insn);
! /* If we've already seen an available expression in this block,
! we can delete this one (It occurs earlier in the block). We'll
! copy the SRC expression to an unused register in case there
! are any side effects. */
! if (TEST_BIT (ae_gen[bb->index], ptr->index))
{
! rtx r = gen_reg_rtx (GET_MODE (ptr->pattern));
! if (gcse_file)
! fprintf (gcse_file, "Removing redundant store:\n");
! replace_store_insn (r, XEXP (st, 0), bb);
! continue;
}
+ SET_BIT (ae_gen[bb->index], ptr->index);
}
! for (st = ANTIC_STORE_LIST (ptr); st != NULL; st = XEXP (st, 1))
! {
! insn = XEXP (st, 0);
! bb = BLOCK_FOR_INSN (insn);
! SET_BIT (st_antloc[bb->index], ptr->index);
! }
}
ae_kill = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
*************** build_store_vectors ()
*** 7309,7350 ****
transp = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
sbitmap_vector_zero (transp, last_basic_block);
! for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
! FOR_EACH_BB (b)
! {
! if (store_killed_after (ptr->pattern, b->head, b))
! {
! /* The anticipatable expression is not killed if it's gen'd. */
! /*
! We leave this check out for now. If we have a code sequence
! in a block which looks like:
! ST MEMa = x
! L y = MEMa
! ST MEMa = z
! We should flag this as having an ANTIC expression, NOT
! transparent, NOT killed, and AVAIL.
! Unfortunately, since we haven't re-written all loads to
! use the reaching reg, we'll end up doing an incorrect
! Load in the middle here if we push the store down. It happens in
! gcc.c-torture/execute/960311-1.c with -O3
! If we always kill it in this case, we'll sometimes do
! unnecessary work, but it shouldn't actually hurt anything.
! if (!TEST_BIT (ae_gen[b], ptr->index)). */
! SET_BIT (ae_kill[b->index], ptr->index);
! }
! else
! SET_BIT (transp[b->index], ptr->index);
! }
- /* Any block with no exits calls some non-returning function, so
- we better mark the store killed here, or we might not store to
- it at all. If we knew it was abort, we wouldn't have to store,
- but we don't know that for sure. */
if (gcse_file)
{
- fprintf (gcse_file, "ST_avail and ST_antic (shown under loads..)\n");
- print_ldst_list (gcse_file);
dump_sbitmap_vector (gcse_file, "st_antloc", "", st_antloc, last_basic_block);
dump_sbitmap_vector (gcse_file, "st_kill", "", ae_kill, last_basic_block);
dump_sbitmap_vector (gcse_file, "Transpt", "", transp, last_basic_block);
--- 7500,7532 ----
transp = (sbitmap *) sbitmap_vector_alloc (last_basic_block, num_stores);
sbitmap_vector_zero (transp, last_basic_block);
+ regs_set_in_block = xmalloc (sizeof (int) * max_gcse_regno);
! FOR_EACH_BB (bb)
! {
! for (regno = 0; regno < max_gcse_regno; regno++)
! regs_set_in_block[regno] = TEST_BIT (reg_set_in_block[bb->index], regno);
!
! for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
! {
! if (store_killed_after (ptr->pattern, ptr->pattern_regs, bb->head,
! bb, regs_set_in_block, NULL))
! {
! /* It should not be neccessary to consider the expression
! killed if it is both anticipatable and available. */
! if (!TEST_BIT (st_antloc[bb->index], ptr->index)
! || !TEST_BIT (ae_gen[bb->index], ptr->index))
! SET_BIT (ae_kill[bb->index], ptr->index);
! }
! else
! SET_BIT (transp[bb->index], ptr->index);
! }
! }
!
! free (regs_set_in_block);
if (gcse_file)
{
dump_sbitmap_vector (gcse_file, "st_antloc", "", st_antloc, last_basic_block);
dump_sbitmap_vector (gcse_file, "st_kill", "", ae_kill, last_basic_block);
dump_sbitmap_vector (gcse_file, "Transpt", "", transp, last_basic_block);
*************** insert_store (expr, e)
*** 7405,7411 ****
return 0;
reg = expr->reaching_reg;
! insn = gen_move_insn (expr->pattern, reg);
/* If we are inserting this expression on ALL predecessor edges of a BB,
insert it at the start of the BB, and reset the insert bits on the other
--- 7587,7593 ----
return 0;
reg = expr->reaching_reg;
! insn = gen_move_insn (copy_rtx (expr->pattern), reg);
/* If we are inserting this expression on ALL predecessor edges of a BB,
insert it at the start of the BB, and reset the insert bits on the other
*************** delete_store (expr, bb)
*** 7493,7501 ****
if (expr->reaching_reg == NULL_RTX)
expr->reaching_reg = gen_reg_rtx (GET_MODE (expr->pattern));
-
- /* If there is more than 1 store, the earlier ones will be dead,
- but it doesn't hurt to replace them here. */
reg = expr->reaching_reg;
for (i = AVAIL_STORE_LIST (expr); i; i = XEXP (i, 1))
--- 7675,7680 ----
*************** store_motion ()
*** 7557,7563 ****
init_alias_analysis ();
! /* Find all the stores that are live to the end of their block. */
num_stores = compute_store_table ();
if (num_stores == 0)
{
--- 7736,7742 ----
init_alias_analysis ();
! /* Find all the available and anticipatable stores. */
num_stores = compute_store_table ();
if (num_stores == 0)
{
*************** store_motion ()
*** 7566,7574 ****
return;
}
! /* Now compute whats actually available to move. */
! add_noreturn_fake_exit_edges ();
build_store_vectors ();
edge_list = pre_edge_rev_lcm (gcse_file, num_stores, transp, ae_gen,
st_antloc, ae_kill, &pre_insert_map,
--- 7745,7753 ----
return;
}
! /* Now compute kill & transp vectors. */
build_store_vectors ();
+ add_noreturn_fake_exit_edges ();
edge_list = pre_edge_rev_lcm (gcse_file, num_stores, transp, ae_gen,
st_antloc, ae_kill, &pre_insert_map,