aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorDaniel Berlin <dan@cgsoftware.com>2001-08-03 16:52:01 +0000
committerDaniel Berlin <dberlin@gcc.gnu.org>2001-08-03 16:52:01 +0000
commitadfcce6198effc2aaeb4961ab7a8ec05723d6573 (patch)
treea59239bd456400b3c2358c1d1d7db15e2eba19d4 /gcc
parent667ccf73ebde1d2340f3e1ff41e4471724bb7126 (diff)
downloadgcc-adfcce6198effc2aaeb4961ab7a8ec05723d6573.zip
gcc-adfcce6198effc2aaeb4961ab7a8ec05723d6573.tar.gz
gcc-adfcce6198effc2aaeb4961ab7a8ec05723d6573.tar.bz2
gcse.c: Include df.h for use as a dataflow analyzer.
2001-07-16 Daniel Berlin <dan@cgsoftware.com> * gcse.c: Include df.h for use as a dataflow analyzer. Remove regvec. Declaration of reg_set_info: gone. New df_analyzer variable used by store motion. (reg_set_info): Deleted. (mark_mem_regs): New function, analyze regs used by a mem. (store_ops_ok): Use dataflow analyzer results to determine if necessary regs are changed in the block. (find_moveable_store): Remove check for symbol ref, we can handle much more complex expressions now. (compute_store_table): Remove most of the code, it's unnecessary now that the dataflow analyzer records the info for us. (store_killed_after): Add parameter to say whether to do the store_ops_okay test, used to speed up testing when we already know the answer, and just want to know if the store itself was killed. (build_store_vector): Largely rewritten to calculate the various vectors properly, and somewhat optimized. (store_motion): Init the df_analyzer, get REG_DEF chains. Also handle trapping expressions (since mems almost always trap) (simple_mem): Redefine what a simple mem is. From-SVN: r44603
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog23
-rw-r--r--gcc/gcse.c453
2 files changed, 335 insertions, 141 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 3ffebfd..58e4658 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,26 @@
+2001-07-16 Daniel Berlin <dan@cgsoftware.com>
+
+ * gcse.c: Include df.h for use as a dataflow analyzer.
+ Remove regvec.
+ Declaration of reg_set_info: gone.
+ New df_analyzer variable used by store motion.
+ (reg_set_info): Deleted.
+ (mark_mem_regs): New function, analyze regs used by a mem.
+ (store_ops_ok): Use dataflow analyzer results to determine if
+ necessary regs are changed in the block.
+ (find_moveable_store): Remove check for symbol ref, we can handle
+ much more complex expressions now.
+ (compute_store_table): Remove most of the code, it's unnecessary
+ now that the dataflow analyzer records the info for us.
+ (store_killed_after): Add parameter to say whether to do the
+ store_ops_okay test, used to speed up testing when we already know
+ the answer, and just want to know if the store itself was killed.
+ (build_store_vector): Largely rewritten to calculate the various
+ vectors properly, and somewhat optimized.
+ (store_motion): Init the df_analyzer, get REG_DEF chains.
+ Also handle trapping expressions (since mems almost always trap)
+ (simple_mem): Redefine what a simple mem is.
+
2001-08-03 DJ Delorie <dj@redhat.com>
* ifcvt.c (noce_get_alt_condition): Don't make an auxiliary
diff --git a/gcc/gcse.c b/gcc/gcse.c
index 4fe1b3b..349eae6 100644
--- a/gcc/gcse.c
+++ b/gcc/gcse.c
@@ -160,8 +160,8 @@ Boston, MA 02111-1307, USA. */
#include "expr.h"
#include "ggc.h"
#include "params.h"
-
#include "obstack.h"
+#include "df.h"
#define obstack_chunk_alloc gmalloc
#define obstack_chunk_free free
@@ -305,6 +305,10 @@ static char can_copy_p[(int) NUM_MACHINE_MODES];
/* Non-zero if can_copy_p has been initialized. */
static int can_copy_init_p;
+/* Dataflow analyzer */
+struct df *df_analyzer;
+
+
struct reg_use {rtx reg_rtx; };
/* Hash table of expressions. */
@@ -466,8 +470,8 @@ struct ls_expr
{
struct expr * expr; /* Gcse expression reference for LM. */
rtx pattern; /* Pattern of this mem. */
- rtx loads; /* INSN list of loads seen. */
- rtx stores; /* INSN list of stores seen. */
+ rtx loads; /* INSN list for where load appears */
+ rtx stores; /* INSN list for where store appears */
struct ls_expr * next; /* Next in the list. */
int invalid; /* Invalid for some reason. */
int index; /* If it maps to a bitmap index. */
@@ -676,14 +680,13 @@ static void invalidate_any_buried_refs PARAMS ((rtx));
static void compute_ld_motion_mems PARAMS ((void));
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 int store_ops_ok PARAMS ((rtx, basic_block, rtx, int));
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_after PARAMS ((rtx, rtx, basic_block, int));
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));
@@ -1466,7 +1469,6 @@ mems_conflict_for_gcse_p (dest, setter, data)
elsewhere. */
if (GET_CODE (dest) != MEM)
return;
-
/* If we are setting a MEM in our list of specially recognized MEMs,
don't mark as killed this time. */
@@ -1476,7 +1478,6 @@ mems_conflict_for_gcse_p (dest, setter, data)
gcse_mems_conflict_p = 1;
return;
}
-
if (true_dependence (dest, GET_MODE (dest), gcse_mem_operand,
rtx_addr_varies_p))
gcse_mems_conflict_p = 1;
@@ -1754,6 +1755,7 @@ hash_expr_1 (x, mode, do_not_record_p)
hash += hash_string_1 (XSTR (x, i));
else if (fmt[i] == 'i')
hash += (unsigned int) XINT (x, i);
+ else if (fmt[i] == 't');
else
abort ();
}
@@ -1912,8 +1914,9 @@ expr_equiv_p (x, y)
break;
case '0':
+ case 't':
break;
-
+
default:
abort ();
}
@@ -5896,9 +5899,9 @@ static void
free_ldst_entry (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);
}
@@ -6019,10 +6022,11 @@ simple_mem (x)
if (GET_MODE (x) == BLKmode)
return 0;
-
- if (!rtx_varies_p (XEXP (x, 0), 0))
+#if 0
+ /* See comment in find_moveable_store */
+ if (!rtx_addr_varies_p (XEXP (x, 0), 0))
return 1;
-
+#endif
return 0;
}
@@ -6104,7 +6108,6 @@ compute_ld_motion_mems ()
/* Make sure there isn't a buried load somewhere. */
invalidate_any_buried_refs (src);
}
-
/* Check for stores. Don't worry about aliased ones, they
will block any movement we might do later. We only care
about this exact pattern since those are the only
@@ -6251,37 +6254,59 @@ update_ld_motion_stores (expr)
/* 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;
/* Global holding the number of store expressions we are dealing with. */
static int num_stores;
-/* Checks to set if we need to mark a register set. Called from note_stores. */
-static void
-reg_set_info (dest, setter, data)
- rtx dest, setter ATTRIBUTE_UNUSED;
- void * data ATTRIBUTE_UNUSED;
+/* Mark which registers are used by the mem, in the sbitmap used. */
+static int
+mark_mem_regs (x, used)
+ rtx x;
+ sbitmap used;
{
- if (GET_CODE (dest) == SUBREG)
- dest = SUBREG_REG (dest);
+ register const char *fmt;
+ int i, j;
- if (GET_CODE (dest) == REG)
- SET_BIT (*regvec, REGNO (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;
}
+
/* Return non-zero if the register operands of expression X are killed
- anywhere in basic block BB. */
+ before/after insn in basic block BB. */
static int
-store_ops_ok (x, bb)
+store_ops_ok (x, bb,insn, before)
rtx x;
basic_block bb;
+ rtx insn;
+ int before;
{
int i;
enum rtx_code code;
@@ -6297,10 +6322,46 @@ store_ops_ok (x, bb)
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));
+ {
+ /* Okay, since the reg def chains are ordered by bb/insn
+ (since that's how it calculates them, and even if it didn't,
+ 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:
x = XEXP (x, 0);
goto repeat;
@@ -6344,7 +6405,7 @@ store_ops_ok (x, bb)
goto repeat;
}
- if (! store_ops_ok (tem, bb))
+ if (! store_ops_ok (tem, bb, insn, before))
return 0;
}
else if (fmt[i] == 'E')
@@ -6353,7 +6414,7 @@ store_ops_ok (x, bb)
for (j = 0; j < XVECLEN (x, i); j++)
{
- if (! store_ops_ok (XVECEXP (x, i, j), bb))
+ if (! store_ops_ok (XVECEXP (x, i, j), bb, insn, before))
return 0;
}
}
@@ -6362,7 +6423,9 @@ store_ops_ok (x, bb)
return 1;
}
-/* Determine whether insn is MEM store pattern that we will consider moving. */
+/* Determine whether insn is MEM store pattern that we will consider
+ moving. We'll consider moving pretty much anything that we can
+ move safely. */
static void
find_moveable_store (insn)
@@ -6371,6 +6434,9 @@ find_moveable_store (insn)
struct ls_expr * ptr;
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
|| GET_CODE (SET_SRC (dest)) == ASM_OPERANDS)
return;
@@ -6379,66 +6445,43 @@ find_moveable_store (insn)
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))
+#if 0
+ /* ??? Is this conservative, or just correct? We get more
+ *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;
-
+#endif
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. */
+/* Perform store motion.
+ Store motion is modeled as a lazy code motion problem, like PRE is
+ 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
compute_store_table ()
{
int bb, ret;
- unsigned regno;
rtx insn, pat;
-
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;
-
/* Find all the stores we care about. */
for (bb = 0; bb < n_basic_blocks; bb++)
{
- regvec = & (reg_set_in_block[bb]);
for (insn = BLOCK_END (bb);
insn && insn != PREV_INSN (BLOCK_HEAD (bb));
insn = PREV_INSN (insn))
{
-#ifdef NON_SAVING_SETJMP
- 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')
+ /* Ignore anything that is not a normal insn. */
+ if (!INSN_P (insn))
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);
- 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);
@@ -6456,7 +6499,8 @@ compute_store_table ()
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
load_kills_store (x, store_pattern)
@@ -6467,8 +6511,8 @@ load_kills_store (x, store_pattern)
return 0;
}
-/* Go through the entire insn X, looking for any loads which might alias
- STORE_PATTERN. Return 1 if found. */
+/* Go through the entire insn X, looking for any loads which might
+ alias, and therefore, kill, STORE_PATTERN. Return 1 if found. */
static int
find_loads (x, store_pattern)
@@ -6524,9 +6568,10 @@ store_killed_in_insn (x, insn)
rtx pat = PATTERN (insn);
/* Check for memory stores to aliased objects. */
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
@@ -6537,31 +6582,31 @@ store_killed_in_insn (x, insn)
within basic block BB. */
static int
-store_killed_after (x, insn, bb)
+store_killed_after (x, insn, bb, testops)
rtx x, insn;
basic_block bb;
+ int testops;
{
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 lookinng for aliasing in each insn. */
- if (!store_ops_ok (XEXP (x, 0), bb))
+
+ if (testops)
+ /* Check if the register operands of the store are OK in this block.*/
+ if (!store_ops_ok (XEXP (x, 0), bb, insn, 0))
return 1;
- for ( ; insn && insn != NEXT_INSN (last); insn = NEXT_INSN (insn))
+ 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
+/* Returns 1 if the expression X is loaded or clobbered before INSN
within basic block BB. */
static int
store_killed_before (x, insn, bb)
@@ -6572,16 +6617,14 @@ store_killed_before (x, insn, bb)
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 lookinng for aliasing in each insn. */
- if (!store_ops_ok (XEXP (x, 0), bb))
+ /* Check if the register operands of the store are OK in this block.*/
+ if (!store_ops_ok (XEXP (x, 0), bb, insn, 1))
return 1;
- for ( ; insn && insn != PREV_INSN (first); insn = PREV_INSN (insn))
+ for (insn = PREV_INSN (insn) ;
+ insn && insn != PREV_INSN (first);
+ insn = PREV_INSN (insn))
+
if (store_killed_in_insn (x, insn))
return 1;
@@ -6598,9 +6641,11 @@ static void
build_store_vectors ()
{
basic_block bb;
- int b;
+ int b,i,j;
rtx insn, st;
struct ls_expr * ptr;
+ sbitmap tested, *result;
+ sbitmap used;
/* Build the gen_vector. This is any store in the table which is not killed
by aliasing later in its block. */
@@ -6609,7 +6654,16 @@ build_store_vectors ()
st_antloc = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks, num_stores);
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))
{
/* Put all the stores into either the antic list, or the avail list,
@@ -6621,8 +6675,7 @@ build_store_vectors ()
{
insn = XEXP (st, 0);
bb = BLOCK_FOR_INSN (insn);
-
- if (!store_killed_after (ptr->pattern, insn, bb))
+ if (!store_killed_after (ptr->pattern, insn, bb, 1))
{
/* If we've already seen an availale expression in this block,
we can delete the one we saw already (It occurs earlier in
@@ -6666,50 +6719,142 @@ build_store_vectors ()
ae_kill = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks, num_stores);
sbitmap_vector_zero (ae_kill, n_basic_blocks);
+
transp = (sbitmap *) sbitmap_vector_alloc (n_basic_blocks, num_stores);
- sbitmap_vector_zero (transp, n_basic_blocks);
+ sbitmap_vector_ones (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 (b = 0; b < n_basic_blocks; b++)
{
- if (store_killed_after (ptr->pattern, BLOCK_HEAD (b), BASIC_BLOCK (b)))
+ /* Make sure we don't have a load-only expr, which we never seem
+ to, but i don't think there's actually a guarantee */
+ if (ptr->stores != NULL)
{
- /* 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
- uneccessary work, but it shouldn't actually hurt anything.
- if (!TEST_BIT (ae_gen[b], ptr->index)). */
- SET_BIT (ae_kill[b], ptr->index);
- }
- else
- SET_BIT (transp[b], 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, n_basic_blocks);
- dump_sbitmap_vector (gcse_file, "st_kill", "", ae_kill, n_basic_blocks);
- dump_sbitmap_vector (gcse_file, "Transpt", "", transp, n_basic_blocks);
- dump_sbitmap_vector (gcse_file, "st_avloc", "", ae_gen, n_basic_blocks);
+ /* First mark the regs used by the mem */
+ mark_mem_regs (ptr->pattern, used);
+ /* Now see if it had any regs */
+ if (!(sbitmap_first_set_bit (used) == -1))
+ {
+ /* For each register, see if we've tested it */
+ EXECUTE_IF_SET_IN_SBITMAP (used, 0, i,
+ {
+ if (TEST_BIT (tested, i))
+ {
+ /* Already tested the register, so check the
+ result, and if we had an okay result, check the
+ store itself. */
+ for (j = 0; j < n_basic_blocks; j++)
+ {
+ if (!TEST_BIT (result[j], i)
+ || 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);
+ }
+ }
+ }
+ else
+ {
+ /* We haven't tested it yet, so mark it tested,
+ and perform the tests */
+ SET_BIT (tested, i);
+ /* Check if it's okay in each BB */
+ for (j = 0; j < n_basic_blocks; j++)
+ {
+ 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
the BLOCK_HEAD if needed. */
@@ -6888,7 +7033,6 @@ static void
free_store_memory ()
{
free_ldst_mems ();
-
if (ae_gen)
sbitmap_vector_free (ae_gen);
if (ae_kill)
@@ -6901,8 +7045,6 @@ free_store_memory ()
sbitmap_vector_free (pre_insert_map);
if (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;
pre_insert_map = pre_delete_map = reg_set_in_block = NULL;
@@ -6916,8 +7058,10 @@ store_motion ()
{
int x;
struct ls_expr * ptr;
- int update_flow = 0;
+ sbitmap trapping_expr;
+ int i;
+ int update_flow = 0;
if (gcse_file)
{
fprintf (gcse_file, "before store motion\n");
@@ -6926,12 +7070,13 @@ store_motion ()
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. */
num_stores = compute_store_table ();
if (num_stores == 0)
{
- sbitmap_vector_free (reg_set_in_block);
+ df_finish (df_analyzer);
end_alias_analysis ();
return;
}
@@ -6940,6 +7085,31 @@ store_motion ()
add_noreturn_fake_exit_edges ();
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,
st_antloc, ae_kill, &pre_insert_map,
&pre_delete_map);
@@ -6958,9 +7128,10 @@ store_motion ()
if (update_flow)
commit_edge_insertions ();
-
+ sbitmap_free (trapping_expr);
free_store_memory ();
free_edge_list (edge_list);
remove_fake_edges ();
end_alias_analysis ();
+ df_finish (df_analyzer);
}