Commit aaa4ca30 by Andreas Jaeger Committed by Andreas Jaeger

As requested by Daniel Berlin since bootstrapping on i686-linux-gnu fails:

	As requested by Daniel Berlin since bootstrapping on i686-linux-gnu
	fails:
	* gcse.c: Revert Daniel's last patch.

From-SVN: r44626
parent da9314d5
2001-08-04 Andreas Jaeger <aj@suse.de>
* gcse.c: Revert Daniel's last patch.
2001-08-03 Zack Weinberg <zackw@stanford.edu> 2001-08-03 Zack Weinberg <zackw@stanford.edu>
* sparc-protos.h: Add prototypes for fp_zero_operand and * sparc-protos.h: Add prototypes for fp_zero_operand and
......
...@@ -160,8 +160,8 @@ Boston, MA 02111-1307, USA. */ ...@@ -160,8 +160,8 @@ Boston, MA 02111-1307, USA. */
#include "expr.h" #include "expr.h"
#include "ggc.h" #include "ggc.h"
#include "params.h" #include "params.h"
#include "obstack.h" #include "obstack.h"
#include "df.h"
#define obstack_chunk_alloc gmalloc #define obstack_chunk_alloc gmalloc
#define obstack_chunk_free free #define obstack_chunk_free free
...@@ -305,10 +305,6 @@ static char can_copy_p[(int) NUM_MACHINE_MODES]; ...@@ -305,10 +305,6 @@ static char can_copy_p[(int) NUM_MACHINE_MODES];
/* Non-zero if can_copy_p has been initialized. */ /* Non-zero if can_copy_p has been initialized. */
static int can_copy_init_p; static int can_copy_init_p;
/* Dataflow analyzer */
struct df *df_analyzer;
struct reg_use {rtx reg_rtx; }; struct reg_use {rtx reg_rtx; };
/* Hash table of expressions. */ /* Hash table of expressions. */
...@@ -470,8 +466,8 @@ struct ls_expr ...@@ -470,8 +466,8 @@ struct ls_expr
{ {
struct expr * expr; /* Gcse expression reference for LM. */ struct expr * expr; /* Gcse expression reference for LM. */
rtx pattern; /* Pattern of this mem. */ rtx pattern; /* Pattern of this mem. */
rtx loads; /* INSN list for where load appears */ rtx loads; /* INSN list of loads seen. */
rtx stores; /* INSN list for where store appears */ rtx stores; /* INSN list of stores seen. */
struct ls_expr * next; /* Next in the list. */ struct ls_expr * next; /* Next in the list. */
int invalid; /* Invalid for some reason. */ int invalid; /* Invalid for some reason. */
int index; /* If it maps to a bitmap index. */ int index; /* If it maps to a bitmap index. */
...@@ -680,13 +676,14 @@ static void invalidate_any_buried_refs PARAMS ((rtx)); ...@@ -680,13 +676,14 @@ static void invalidate_any_buried_refs PARAMS ((rtx));
static void compute_ld_motion_mems PARAMS ((void)); static void compute_ld_motion_mems PARAMS ((void));
static void trim_ld_motion_mems PARAMS ((void)); static void trim_ld_motion_mems PARAMS ((void));
static void update_ld_motion_stores PARAMS ((struct expr *)); static void update_ld_motion_stores PARAMS ((struct expr *));
static int store_ops_ok PARAMS ((rtx, basic_block, rtx, int)); 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 void find_moveable_store PARAMS ((rtx));
static int compute_store_table PARAMS ((void)); static int compute_store_table PARAMS ((void));
static int load_kills_store PARAMS ((rtx, rtx)); static int load_kills_store PARAMS ((rtx, rtx));
static int find_loads PARAMS ((rtx, rtx)); static int find_loads PARAMS ((rtx, rtx));
static int store_killed_in_insn PARAMS ((rtx, rtx)); static int store_killed_in_insn PARAMS ((rtx, rtx));
static int store_killed_after PARAMS ((rtx, rtx, basic_block, int)); static int store_killed_after PARAMS ((rtx, rtx, basic_block));
static int store_killed_before PARAMS ((rtx, rtx, basic_block)); static int store_killed_before PARAMS ((rtx, rtx, basic_block));
static void build_store_vectors PARAMS ((void)); static void build_store_vectors PARAMS ((void));
static void insert_insn_start_bb PARAMS ((rtx, basic_block)); static void insert_insn_start_bb PARAMS ((rtx, basic_block));
...@@ -1469,6 +1466,7 @@ mems_conflict_for_gcse_p (dest, setter, data) ...@@ -1469,6 +1466,7 @@ mems_conflict_for_gcse_p (dest, setter, data)
elsewhere. */ elsewhere. */
if (GET_CODE (dest) != MEM) if (GET_CODE (dest) != MEM)
return; return;
/* If we are setting a MEM in our list of specially recognized MEMs, /* If we are setting a MEM in our list of specially recognized MEMs,
don't mark as killed this time. */ don't mark as killed this time. */
...@@ -1478,6 +1476,7 @@ mems_conflict_for_gcse_p (dest, setter, data) ...@@ -1478,6 +1476,7 @@ mems_conflict_for_gcse_p (dest, setter, data)
gcse_mems_conflict_p = 1; gcse_mems_conflict_p = 1;
return; return;
} }
if (true_dependence (dest, GET_MODE (dest), gcse_mem_operand, if (true_dependence (dest, GET_MODE (dest), gcse_mem_operand,
rtx_addr_varies_p)) rtx_addr_varies_p))
gcse_mems_conflict_p = 1; gcse_mems_conflict_p = 1;
...@@ -1755,7 +1754,6 @@ hash_expr_1 (x, mode, do_not_record_p) ...@@ -1755,7 +1754,6 @@ hash_expr_1 (x, mode, do_not_record_p)
hash += hash_string_1 (XSTR (x, i)); hash += hash_string_1 (XSTR (x, i));
else if (fmt[i] == 'i') else if (fmt[i] == 'i')
hash += (unsigned int) XINT (x, i); hash += (unsigned int) XINT (x, i);
else if (fmt[i] == 't');
else else
abort (); abort ();
} }
...@@ -1914,9 +1912,8 @@ expr_equiv_p (x, y) ...@@ -1914,9 +1912,8 @@ expr_equiv_p (x, y)
break; break;
case '0': case '0':
case 't':
break; break;
default: default:
abort (); abort ();
} }
...@@ -5899,9 +5896,9 @@ static void ...@@ -5899,9 +5896,9 @@ static void
free_ldst_entry (ptr) free_ldst_entry (ptr)
struct ls_expr * ptr; struct ls_expr * ptr;
{ {
free_INSN_LIST_list (& ptr->loads);
free_INSN_LIST_list (& ptr->stores);
free_INSN_LIST_list (&ptr->stores);
free_INSN_LIST_list (&ptr->loads);
free (ptr); free (ptr);
} }
...@@ -6022,11 +6019,10 @@ simple_mem (x) ...@@ -6022,11 +6019,10 @@ simple_mem (x)
if (GET_MODE (x) == BLKmode) if (GET_MODE (x) == BLKmode)
return 0; return 0;
#if 0
/* See comment in find_moveable_store */ if (!rtx_varies_p (XEXP (x, 0), 0))
if (!rtx_addr_varies_p (XEXP (x, 0), 0))
return 1; return 1;
#endif
return 0; return 0;
} }
...@@ -6108,6 +6104,7 @@ compute_ld_motion_mems () ...@@ -6108,6 +6104,7 @@ compute_ld_motion_mems ()
/* Make sure there isn't a buried load somewhere. */ /* Make sure there isn't a buried load somewhere. */
invalidate_any_buried_refs (src); invalidate_any_buried_refs (src);
} }
/* Check for stores. Don't worry about aliased ones, they /* Check for stores. Don't worry about aliased ones, they
will block any movement we might do later. We only care will block any movement we might do later. We only care
about this exact pattern since those are the only about this exact pattern since those are the only
...@@ -6254,59 +6251,37 @@ update_ld_motion_stores (expr) ...@@ -6254,59 +6251,37 @@ update_ld_motion_stores (expr)
/* Store motion code. */ /* 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. */ /* Used in computing the reverse edge graph bit vectors. */
static sbitmap * st_antloc; static sbitmap * st_antloc;
/* Global holding the number of store expressions we are dealing with. */ /* Global holding the number of store expressions we are dealing with. */
static int num_stores; static int num_stores;
/* Checks to set if we need to mark a register set. Called from note_stores. */
/* Mark which registers are used by the mem, in the sbitmap used. */ static void
static int reg_set_info (dest, setter, data)
mark_mem_regs (x, used) rtx dest, setter ATTRIBUTE_UNUSED;
rtx x; void * data ATTRIBUTE_UNUSED;
sbitmap used;
{ {
register const char *fmt; if (GET_CODE (dest) == SUBREG)
int i, j; dest = SUBREG_REG (dest);
if (GET_CODE (x) == REG)
{
if (!TEST_BIT (used, REGNO (x)))
{
SET_BIT (used, REGNO (x));
return 1;
}
return 0;
}
fmt = GET_RTX_FORMAT (GET_CODE (x));
for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
{
if (fmt[i] == 'e')
{
if (mark_mem_regs (XEXP (x, i),used))
return 1;
}
else if (fmt[i] == 'E')
for (j = XVECLEN (x, i) - 1; j >= 0; j--)
if (mark_mem_regs (XVECEXP (x, i, j),used))
return 1;
}
return 0; if (GET_CODE (dest) == REG)
SET_BIT (*regvec, REGNO (dest));
} }
/* Return non-zero if the register operands of expression X are killed /* Return non-zero if the register operands of expression X are killed
before/after insn in basic block BB. */ anywhere in basic block BB. */
static int static int
store_ops_ok (x, bb,insn, before) store_ops_ok (x, bb)
rtx x; rtx x;
basic_block bb; basic_block bb;
rtx insn;
int before;
{ {
int i; int i;
enum rtx_code code; enum rtx_code code;
...@@ -6322,46 +6297,10 @@ store_ops_ok (x, bb,insn, before) ...@@ -6322,46 +6297,10 @@ store_ops_ok (x, bb,insn, before)
switch (code) switch (code)
{ {
case REG: case REG:
{ /* If a reg has changed after us in this
/* Okay, since the reg def chains are ordered by bb/insn block, the operand has been killed. */
(since that's how it calculates them, and even if it didn't, return TEST_BIT (reg_set_in_block[bb->index], REGNO (x));
we could just sort them), we just walk until we find a def
in our BB, then walk until we find a def after/before our
insn, and if we find a reg def after/before our insn, in the
same bb, we return the approriate value. If there is no
such def, to prevent walking *every* reg def, we stop once
we are out of our BB again. */
struct df_link *currref;
bool thereyet=FALSE;
for (currref = df_analyzer->regs[REGNO(x)].defs;
currref;
currref = currref->next)
{
if (! (DF_REF_BB (currref->ref) == bb))
{
if (!thereyet)
continue;
else
return 1;
}
if (before)
{
if (INSN_UID (DF_REF_INSN (currref->ref)) >= INSN_UID (insn))
continue;
}
else
{
if (INSN_UID (DF_REF_INSN (currref->ref)) < INSN_UID (insn))
continue;
}
thereyet = TRUE;
if (DF_REF_TYPE (currref->ref) == DF_REF_REG_DEF)
return 0;
}
return 1;
}
case MEM: case MEM:
x = XEXP (x, 0); x = XEXP (x, 0);
goto repeat; goto repeat;
...@@ -6405,7 +6344,7 @@ store_ops_ok (x, bb,insn, before) ...@@ -6405,7 +6344,7 @@ store_ops_ok (x, bb,insn, before)
goto repeat; goto repeat;
} }
if (! store_ops_ok (tem, bb, insn, before)) if (! store_ops_ok (tem, bb))
return 0; return 0;
} }
else if (fmt[i] == 'E') else if (fmt[i] == 'E')
...@@ -6414,7 +6353,7 @@ store_ops_ok (x, bb,insn, before) ...@@ -6414,7 +6353,7 @@ store_ops_ok (x, bb,insn, before)
for (j = 0; j < XVECLEN (x, i); j++) for (j = 0; j < XVECLEN (x, i); j++)
{ {
if (! store_ops_ok (XVECEXP (x, i, j), bb, insn, before)) if (! store_ops_ok (XVECEXP (x, i, j), bb))
return 0; return 0;
} }
} }
...@@ -6423,9 +6362,7 @@ store_ops_ok (x, bb,insn, before) ...@@ -6423,9 +6362,7 @@ store_ops_ok (x, bb,insn, before)
return 1; return 1;
} }
/* Determine whether insn is MEM store pattern that we will consider /* Determine whether insn is MEM store pattern that we will consider moving. */
moving. We'll consider moving pretty much anything that we can
move safely. */
static void static void
find_moveable_store (insn) find_moveable_store (insn)
...@@ -6434,9 +6371,6 @@ find_moveable_store (insn) ...@@ -6434,9 +6371,6 @@ find_moveable_store (insn)
struct ls_expr * ptr; struct ls_expr * ptr;
rtx dest = PATTERN (insn); rtx dest = PATTERN (insn);
/* It's it's not a set, it's not a mem store we want to consider.
Also, if it's an ASM, we certainly don't want to try to touch
it. */
if (GET_CODE (dest) != SET if (GET_CODE (dest) != SET
|| GET_CODE (SET_SRC (dest)) == ASM_OPERANDS) || GET_CODE (SET_SRC (dest)) == ASM_OPERANDS)
return; return;
...@@ -6445,43 +6379,66 @@ find_moveable_store (insn) ...@@ -6445,43 +6379,66 @@ find_moveable_store (insn)
if (GET_CODE (dest) != MEM || MEM_VOLATILE_P (dest) if (GET_CODE (dest) != MEM || MEM_VOLATILE_P (dest)
|| GET_MODE (dest) == BLKmode) || GET_MODE (dest) == BLKmode)
return;
if (GET_CODE (XEXP (dest, 0)) != SYMBOL_REF)
return; return;
#if 0
/* ??? Is this conservative, or just correct? We get more if (rtx_varies_p (XEXP (dest, 0), 0))
*candidates* without it, but i don't think we ever remove any
stores where the address did vary. */
if (rtx_addr_varies_p (XEXP (dest, 0), 0))
return; return;
#endif
ptr = ldst_entry (dest); ptr = ldst_entry (dest);
ptr->stores = alloc_INSN_LIST (insn, ptr->stores); ptr->stores = alloc_INSN_LIST (insn, ptr->stores);
} }
/* Perform store motion. /* Perform store motion. Much like gcse, except we move expressions the
Store motion is modeled as a lazy code motion problem, like PRE is other way by looking at the flowgraph in reverse. */
above. The main diffence is that we want to move stores down as far
as possible, so we have LCM work on the reverse flowgraph. */
static int static int
compute_store_table () compute_store_table ()
{ {
int bb, ret; int bb, ret;
unsigned regno;
rtx insn, pat; rtx insn, pat;
max_gcse_regno = max_reg_num (); max_gcse_regno = max_reg_num ();
reg_set_in_block = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks,
max_gcse_regno);
sbitmap_vector_zero (reg_set_in_block, n_basic_blocks);
pre_ldst_mems = 0; pre_ldst_mems = 0;
/* Find all the stores we care about. */ /* Find all the stores we care about. */
for (bb = 0; bb < n_basic_blocks; bb++) for (bb = 0; bb < n_basic_blocks; bb++)
{ {
regvec = & (reg_set_in_block[bb]);
for (insn = BLOCK_END (bb); for (insn = BLOCK_END (bb);
insn && insn != PREV_INSN (BLOCK_HEAD (bb)); insn && insn != PREV_INSN (BLOCK_HEAD (bb));
insn = PREV_INSN (insn)) insn = PREV_INSN (insn))
{ {
/* Ignore anything that is not a normal insn. */ #ifdef NON_SAVING_SETJMP
if (!INSN_P (insn)) if (NON_SAVING_SETJMP && GET_CODE (insn) == NOTE
&& NOTE_LINE_NUMBER (insn) == NOTE_INSN_SETJMP)
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
SET_BIT (reg_set_in_block[bb], regno);
continue;
}
#endif
/* Ignore anything that is not a normal insn. */
if (GET_RTX_CLASS (GET_CODE (insn)) != 'i')
continue; continue;
if (GET_CODE (insn) == CALL_INSN)
{
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
if (TEST_HARD_REG_BIT (regs_invalidated_by_call, regno))
SET_BIT (reg_set_in_block[bb], regno);
}
pat = PATTERN (insn); pat = PATTERN (insn);
note_stores (pat, reg_set_info, NULL);
/* Now that we've marked regs, look for stores. */ /* Now that we've marked regs, look for stores. */
if (GET_CODE (pat) == SET) if (GET_CODE (pat) == SET)
find_moveable_store (insn); find_moveable_store (insn);
...@@ -6499,8 +6456,7 @@ compute_store_table () ...@@ -6499,8 +6456,7 @@ compute_store_table ()
return ret; return ret;
} }
/* Check to see if the load X is aliased with STORE_PATTERN. /* Check to see if the load X is aliased with STORE_PATTERN. */
If it is, it means that load kills the store.*/
static int static int
load_kills_store (x, store_pattern) load_kills_store (x, store_pattern)
...@@ -6511,8 +6467,8 @@ load_kills_store (x, store_pattern) ...@@ -6511,8 +6467,8 @@ load_kills_store (x, store_pattern)
return 0; return 0;
} }
/* Go through the entire insn X, looking for any loads which might /* Go through the entire insn X, looking for any loads which might alias
alias, and therefore, kill, STORE_PATTERN. Return 1 if found. */ STORE_PATTERN. Return 1 if found. */
static int static int
find_loads (x, store_pattern) find_loads (x, store_pattern)
...@@ -6568,10 +6524,9 @@ store_killed_in_insn (x, insn) ...@@ -6568,10 +6524,9 @@ store_killed_in_insn (x, insn)
rtx pat = PATTERN (insn); rtx pat = PATTERN (insn);
/* Check for memory stores to aliased objects. */ /* Check for memory stores to aliased objects. */
if (GET_CODE (SET_DEST (pat)) == MEM && !expr_equiv_p (SET_DEST (pat), x)) 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)) if (find_loads (SET_DEST (pat), x))
return 1; return 1;
}
return find_loads (SET_SRC (pat), x); return find_loads (SET_SRC (pat), x);
} }
else else
...@@ -6582,31 +6537,31 @@ store_killed_in_insn (x, insn) ...@@ -6582,31 +6537,31 @@ store_killed_in_insn (x, insn)
within basic block BB. */ within basic block BB. */
static int static int
store_killed_after (x, insn, bb, testops) store_killed_after (x, insn, bb)
rtx x, insn; rtx x, insn;
basic_block bb; basic_block bb;
int testops;
{ {
rtx last = bb->end; rtx last = bb->end;
if (insn == last) if (insn == last)
return 0; return 0;
if (testops) /* Check if the register operands of the store are OK in this block.
/* 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
if (!store_ops_ok (XEXP (x, 0), bb, insn, 0)) 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 lookinng for aliasing in each insn. */
if (!store_ops_ok (XEXP (x, 0), bb))
return 1; return 1;
for ( ; for ( ; insn && insn != NEXT_INSN (last); insn = NEXT_INSN (insn))
insn && insn != NEXT_INSN (last);
insn = NEXT_INSN (insn))
if (store_killed_in_insn (x, insn)) if (store_killed_in_insn (x, insn))
return 1; return 1;
return 0; return 0;
} }
/* Returns 1 if the expression X is loaded or clobbered before INSN /* Returns 1 if the expression X is loaded or clobbered on or before INSN
within basic block BB. */ within basic block BB. */
static int static int
store_killed_before (x, insn, bb) store_killed_before (x, insn, bb)
...@@ -6617,14 +6572,16 @@ store_killed_before (x, insn, bb) ...@@ -6617,14 +6572,16 @@ store_killed_before (x, insn, bb)
if (insn == first) if (insn == first)
return store_killed_in_insn (x, insn); return store_killed_in_insn (x, insn);
/* Check if the register operands of the store are OK in this block.*/
if (!store_ops_ok (XEXP (x, 0), bb, insn, 1)) /* 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 lookinng for aliasing in each insn. */
if (!store_ops_ok (XEXP (x, 0), bb))
return 1; return 1;
for (insn = PREV_INSN (insn) ; for ( ; insn && insn != PREV_INSN (first); insn = PREV_INSN (insn))
insn && insn != PREV_INSN (first);
insn = PREV_INSN (insn))
if (store_killed_in_insn (x, insn)) if (store_killed_in_insn (x, insn))
return 1; return 1;
...@@ -6641,11 +6598,9 @@ static void ...@@ -6641,11 +6598,9 @@ static void
build_store_vectors () build_store_vectors ()
{ {
basic_block bb; basic_block bb;
int b,i,j; int b;
rtx insn, st; rtx insn, st;
struct ls_expr * ptr; struct ls_expr * ptr;
sbitmap tested, *result;
sbitmap used;
/* Build the gen_vector. This is any store in the table which is not killed /* Build the gen_vector. This is any store in the table which is not killed
by aliasing later in its block. */ by aliasing later in its block. */
...@@ -6654,16 +6609,7 @@ build_store_vectors () ...@@ -6654,16 +6609,7 @@ build_store_vectors ()
st_antloc = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks, num_stores); st_antloc = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks, num_stores);
sbitmap_vector_zero (st_antloc, n_basic_blocks); sbitmap_vector_zero (st_antloc, n_basic_blocks);
/* Note: In case someone needs something to optimize about store
motion, here's the next place to look. We currently test one more
basic block per store than necessary (at least). Since we know, at
the end of this for loop, whether a store was killed in one of the
basic blocks (We know both whether it's killed before, and killed
after, the insn in the bb it resides in. So unless the insn
consists of multiple store/loads, we know whether it was killed
in the entire bb), we could avoid testing it for kill and transp in
the next for loop. */
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr)) 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, /* Put all the stores into either the antic list, or the avail list,
...@@ -6675,7 +6621,8 @@ build_store_vectors () ...@@ -6675,7 +6621,8 @@ build_store_vectors ()
{ {
insn = XEXP (st, 0); insn = XEXP (st, 0);
bb = BLOCK_FOR_INSN (insn); bb = BLOCK_FOR_INSN (insn);
if (!store_killed_after (ptr->pattern, insn, bb, 1))
if (!store_killed_after (ptr->pattern, insn, bb))
{ {
/* If we've already seen an availale expression in this block, /* If we've already seen an availale expression in this block,
we can delete the one we saw already (It occurs earlier in we can delete the one we saw already (It occurs earlier in
...@@ -6719,142 +6666,50 @@ build_store_vectors () ...@@ -6719,142 +6666,50 @@ build_store_vectors ()
ae_kill = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks, num_stores); ae_kill = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks, num_stores);
sbitmap_vector_zero (ae_kill, n_basic_blocks); sbitmap_vector_zero (ae_kill, n_basic_blocks);
transp = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks, num_stores); transp = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks, num_stores);
sbitmap_vector_ones (transp, n_basic_blocks); sbitmap_vector_zero (transp, n_basic_blocks);
tested = sbitmap_alloc (max_gcse_regno);
sbitmap_zero (tested);
result = sbitmap_vector_alloc (n_basic_blocks, max_gcse_regno);
sbitmap_vector_zero (result, n_basic_blocks);
used = sbitmap_alloc (max_gcse_regno);
sbitmap_zero (used);
/* This whole big nasty thing computes kill and transparent.
It's done in this nasty way because profiling showed store motion
taking twice as long as GCSE, with the cause being 1 million calls
to store_ops_ok taking 30% of the entire runtime of the
compiler.
Since store most expressions use the same registers, there's no
point in checking them 8 million times for the same basic blocks. If
they weren't okay in a BB the last time we checked, they won't be
okay now. Since we check all the bb's on each iteration, we don't
need a vector for which registers we've tested, just the results.
We then proceed to use the results of what store_ops_ok was for a
given reg and bb, and if the results were a kill, we don't even need
to check if the store was killed in the basic block, it'll be
in the kill set because it's regs changed between here and there.
If the whole store had no registers, we just skip store_ops_okay
anyway (since it's checking reg operands), and proceed to see if
it's okay in each bb, setting the approriate bits.
With this in place, we now take almost no time at all to perform
store motion. (It's not on the first page of the profile, it
takes less than a second).
*/
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr)) for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr (ptr))
for (b = 0; b < n_basic_blocks; b++)
{ {
/* Make sure we don't have a load-only expr, which we never seem if (store_killed_after (ptr->pattern, BLOCK_HEAD (b), BASIC_BLOCK (b)))
to, but i don't think there's actually a guarantee */
if (ptr->stores != NULL)
{ {
/* First mark the regs used by the mem */ /* The anticipatable expression is not killed if it's gen'd. */
mark_mem_regs (ptr->pattern, used); /*
/* Now see if it had any regs */ We leave this check out for now. If we have a code sequence
if (!(sbitmap_first_set_bit (used) == -1)) in a block which looks like:
{ ST MEMa = x
/* For each register, see if we've tested it */ L y = MEMa
EXECUTE_IF_SET_IN_SBITMAP (used, 0, i, ST MEMa = z
{ We should flag this as having an ANTIC expression, NOT
if (TEST_BIT (tested, i)) transparent, NOT killed, and AVAIL.
{ Unfortunately, since we haven't re-written all loads to
/* Already tested the register, so check the use the reaching reg, we'll end up doing an incorrect
result, and if we had an okay result, check the Load in the middle here if we push the store down. It happens in
store itself. */ gcc.c-torture/execute/960311-1.c with -O3
for (j = 0; j < n_basic_blocks; j++) If we always kill it in this case, we'll sometimes do
{ uneccessary work, but it shouldn't actually hurt anything.
if (!TEST_BIT (result[j], i) if (!TEST_BIT (ae_gen[b], ptr->index)). */
|| store_killed_after (ptr->pattern, BLOCK_HEAD (j), SET_BIT (ae_kill[b], ptr->index);
BASIC_BLOCK (j), FALSE)) }
{ else
SET_BIT (ae_kill[j], ptr->index); SET_BIT (transp[b], ptr->index);
if (!TEST_BIT (ae_gen[j], ptr->index) }
|| !TEST_BIT (st_antloc[j], ptr->index))
RESET_BIT (transp[j], 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. */
else if (gcse_file)
{ {
/* We haven't tested it yet, so mark it tested, fprintf (gcse_file, "ST_avail and ST_antic (shown under loads..)\n");
and perform the tests */ print_ldst_list (gcse_file);
SET_BIT (tested, i); dump_sbitmap_vector (gcse_file, "st_antloc", "", st_antloc, n_basic_blocks);
/* Check if it's okay in each BB */ dump_sbitmap_vector (gcse_file, "st_kill", "", ae_kill, n_basic_blocks);
for (j = 0; j < n_basic_blocks; j++) dump_sbitmap_vector (gcse_file, "Transpt", "", transp, n_basic_blocks);
{ dump_sbitmap_vector (gcse_file, "st_avloc", "", ae_gen, n_basic_blocks);
if (store_ops_ok (XEXP (ptr->pattern, 0),
BASIC_BLOCK (j), BLOCK_HEAD (j), 0))
{
SET_BIT (result[j], ptr->index);
}
else
{
/* It's not okay, so it's killed and maybe
not transparent */
SET_BIT (ae_kill[j], ptr->index);
if (!TEST_BIT (ae_gen[j], ptr->index)
|| !TEST_BIT (st_antloc[j], ptr->index))
{
RESET_BIT (transp[j], ptr->index);
}
continue;
}
/* The ops were okay, so check the store
itself */
if (store_killed_after (ptr->pattern, BLOCK_HEAD (j),
BASIC_BLOCK (j), FALSE))
{
SET_BIT (ae_kill[j], ptr->index);
if (!TEST_BIT (ae_gen[j], ptr->index)
|| !TEST_BIT (st_antloc[j], ptr->index))
{
RESET_BIT (transp[j], ptr->index);
}
}
}
}
});
/* Reset the used list */
sbitmap_zero (used);
}
/* If it had no registers, we come here, and do the
approriate testing */
else
{
for (j = 0; j < n_basic_blocks; j++)
{
if (store_killed_after (ptr->pattern, BLOCK_HEAD (j),
BASIC_BLOCK (j), FALSE))
{
SET_BIT (ae_kill[j], ptr->index);
if (!TEST_BIT (ae_gen[j], ptr->index)
|| !TEST_BIT (st_antloc[j], ptr->index))
{
RESET_BIT (transp[j], ptr->index);
}
}
}
}
} }
} }
sbitmap_free (tested);
sbitmap_free (used);
sbitmap_vector_free (result);
}
/* Insert an instruction at the begining of a basic block, and update /* Insert an instruction at the begining of a basic block, and update
the BLOCK_HEAD if needed. */ the BLOCK_HEAD if needed. */
...@@ -7033,6 +6888,7 @@ static void ...@@ -7033,6 +6888,7 @@ static void
free_store_memory () free_store_memory ()
{ {
free_ldst_mems (); free_ldst_mems ();
if (ae_gen) if (ae_gen)
sbitmap_vector_free (ae_gen); sbitmap_vector_free (ae_gen);
if (ae_kill) if (ae_kill)
...@@ -7045,6 +6901,8 @@ free_store_memory () ...@@ -7045,6 +6901,8 @@ free_store_memory ()
sbitmap_vector_free (pre_insert_map); sbitmap_vector_free (pre_insert_map);
if (pre_delete_map) if (pre_delete_map)
sbitmap_vector_free (pre_delete_map); sbitmap_vector_free (pre_delete_map);
if (reg_set_in_block)
sbitmap_vector_free (reg_set_in_block);
ae_gen = ae_kill = transp = st_antloc = NULL; ae_gen = ae_kill = transp = st_antloc = NULL;
pre_insert_map = pre_delete_map = reg_set_in_block = NULL; pre_insert_map = pre_delete_map = reg_set_in_block = NULL;
...@@ -7058,10 +6916,8 @@ store_motion () ...@@ -7058,10 +6916,8 @@ store_motion ()
{ {
int x; int x;
struct ls_expr * ptr; struct ls_expr * ptr;
sbitmap trapping_expr;
int i;
int update_flow = 0; int update_flow = 0;
if (gcse_file) if (gcse_file)
{ {
fprintf (gcse_file, "before store motion\n"); fprintf (gcse_file, "before store motion\n");
...@@ -7070,13 +6926,12 @@ store_motion () ...@@ -7070,13 +6926,12 @@ store_motion ()
init_alias_analysis (); init_alias_analysis ();
df_analyzer = df_init();
df_analyse (df_analyzer, 0, DF_RD_CHAIN | DF_HARD_REGS);
/* Find all the stores that are live to the end of their block. */ /* Find all the stores that are live to the end of their block. */
num_stores = compute_store_table (); num_stores = compute_store_table ();
if (num_stores == 0) if (num_stores == 0)
{ {
df_finish (df_analyzer); sbitmap_vector_free (reg_set_in_block);
end_alias_analysis (); end_alias_analysis ();
return; return;
} }
...@@ -7085,31 +6940,6 @@ store_motion () ...@@ -7085,31 +6940,6 @@ store_motion ()
add_noreturn_fake_exit_edges (); add_noreturn_fake_exit_edges ();
build_store_vectors (); build_store_vectors ();
/* Collect expressions which might trap. */
trapping_expr = sbitmap_alloc (num_stores);
sbitmap_zero (trapping_expr);
for (ptr = first_ls_expr (); ptr != NULL; ptr = next_ls_expr(ptr))
{
if (may_trap_p (ptr->pattern))
SET_BIT (trapping_expr, ptr->index);
}
for (i = 0; i < n_basic_blocks; i++)
{
edge e;
/* If the current block is the destination of an abnormal edge, we
kill all trapping expressions because we won't be able to properly
place the instruction on the edge. So make them neither
anticipatable nor transparent. This is fairly conservative. */
for (e = BASIC_BLOCK (i)->pred; e ; e = e->pred_next)
if (e->flags & EDGE_ABNORMAL)
{
sbitmap_difference (st_antloc[i], st_antloc[i], trapping_expr);
sbitmap_difference (transp[i], transp[i], trapping_expr);
break;
}
}
edge_list = pre_edge_rev_lcm (gcse_file, num_stores, transp, ae_gen, edge_list = pre_edge_rev_lcm (gcse_file, num_stores, transp, ae_gen,
st_antloc, ae_kill, &pre_insert_map, st_antloc, ae_kill, &pre_insert_map,
&pre_delete_map); &pre_delete_map);
...@@ -7128,10 +6958,9 @@ store_motion () ...@@ -7128,10 +6958,9 @@ store_motion ()
if (update_flow) if (update_flow)
commit_edge_insertions (); commit_edge_insertions ();
sbitmap_free (trapping_expr);
free_store_memory (); free_store_memory ();
free_edge_list (edge_list); free_edge_list (edge_list);
remove_fake_edges (); remove_fake_edges ();
end_alias_analysis (); end_alias_analysis ();
df_finish (df_analyzer);
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment