--- /dev/null
+/* { dg-do run } */
+/* { dg-options "-O2 -fno-inline -fpredictive-commoning -fdump-tree-pcom-details" } */
+
+int arr[105] = {2, 3, 5, 7, 11};
+int result0[10] = {2, 3, 5, 7, 11};
+int result1[10] = {0, -1, 5, -2, 11, 0};
+int result2[10] = {0, 0, -1, -2, -2, 0};
+int result3[10] = {0, 0, 0, -1, -2, -2, 0};
+int result4[10] = {0, 0, 0, 0, -1, -2, -2, 0};
+int result100[105] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, -2, -2, 0};
+
+extern void abort (void);
+int sum;
+
+void __attribute__((noinline)) foo (int * restrict a, int len, int t1, int t2)
+{
+ int i;
+ for (i = 0; i < len; i++)
+ {
+ a[i] = t1;
+ a[i + 3] = t2;
+ a[i + 1] = -1;
+ sum = sum + a[i] + a[i + 3];
+ }
+}
+
+void check (int *a, int *res, int len, int sval)
+{
+ int i;
+
+ if (sum != sval)
+ abort ();
+
+ for (i = 0; i < len; i++)
+ if (a[i] != res[i])
+ abort ();
+}
+
+int main (void)
+{
+ foo (arr, 0, 0, -2);
+ check (arr, result0, 10, 0);
+
+ foo (arr, 1, 0, -2);
+ check (arr, result1, 10, -2);
+
+ foo (arr, 2, 0, -2);
+ check (arr, result2, 10, -6);
+
+ foo (arr, 3, 0, -2);
+ check (arr, result3, 10, -12);
+
+ foo (arr, 4, 0, -2);
+ check (arr, result4, 10, -20);
+
+ foo (arr, 100, 0, -2);
+ check (arr, result100, 105, -220);
+
+ return 0;
+}
+/* { dg-final { scan-tree-dump "Store-stores chain" "pcom"} } */
The interesting part is this can be viewed either as general store motion
or general dead store elimination in either intra/inter-iterations way.
+ With trivial effort, we also support load inside Store-Store chains if the
+ load is dominated by a store statement in the same iteration of loop. You
+ can see this as a restricted Store-Mixed-Load-Store chain.
+
TODO: For now, we don't support store-store chains in multi-exit loops. We
force to not unroll in case of store-store chain even if other chains might
ask for unroll.
gimple_bb (dataref->stmt));
dataref->pos = comp->refs.length ();
comp->refs.quick_push (dataref);
- if (DR_IS_READ (dr))
- comp->eliminate_store_p = false;
}
for (i = 0; i < n; i++)
return chain->refs[0];
}
-/* Given CHAIN, returns the last ref at DISTANCE, or NULL if it doesn't
+/* Given CHAIN, returns the last write ref at DISTANCE, or NULL if it doesn't
exist. */
static inline dref
-get_chain_last_ref_at (chain_p chain, unsigned distance)
+get_chain_last_write_at (chain_p chain, unsigned distance)
{
- unsigned i;
+ for (unsigned i = chain->refs.length (); i > 0; i--)
+ if (DR_IS_WRITE (chain->refs[i - 1]->ref)
+ && distance == chain->refs[i - 1]->distance)
+ return chain->refs[i - 1];
- for (i = chain->refs.length (); i > 0; i--)
- if (distance == chain->refs[i - 1]->distance)
- break;
+ return NULL;
+}
+
+/* Given CHAIN, returns the last write ref with the same distance before load
+ at index LOAD_IDX, or NULL if it doesn't exist. */
+
+static inline dref
+get_chain_last_write_before_load (chain_p chain, unsigned load_idx)
+{
+ gcc_assert (load_idx < chain->refs.length ());
- return (i > 0) ? chain->refs[i - 1] : NULL;
+ unsigned distance = chain->refs[load_idx]->distance;
+
+ for (unsigned i = load_idx; i > 0; i--)
+ if (DR_IS_WRITE (chain->refs[i - 1]->ref)
+ && distance == chain->refs[i - 1]->distance)
+ return chain->refs[i - 1];
+
+ return NULL;
}
/* Adds REF to the chain CHAIN. */
chain->has_max_use_after = false;
}
+ /* Promote this chain to CT_STORE_STORE if it has multiple stores. */
+ if (DR_IS_WRITE (ref->ref))
+ chain->type = CT_STORE_STORE;
+
/* Don't set the flag for store-store chain since there is no use. */
if (chain->type != CT_STORE_STORE
&& ref->distance == chain->length
}
comp->refs.qsort (order_drefs);
+
+ /* For Store-Store chain, we only support load if it is dominated by a
+ store statement in the same iteration of loop. */
+ if (comp->eliminate_store_p)
+ for (a = NULL, i = 0; i < comp->refs.length (); i++)
+ {
+ if (DR_IS_WRITE (comp->refs[i]->ref))
+ a = comp->refs[i];
+ else if (a == NULL || a->offset != comp->refs[i]->offset)
+ {
+ /* If there is load that is not dominated by a store in the
+ same iteration of loop, clear the flag so no Store-Store
+ chain is generated for this component. */
+ comp->eliminate_store_p = false;
+ break;
+ }
+ }
+
+ /* Determine roots and create chains for components. */
FOR_EACH_VEC_ELT (comp->refs, i, a)
{
if (!chain
+ || (chain->type == CT_LOAD && DR_IS_WRITE (a->ref))
|| (!comp->eliminate_store_p && DR_IS_WRITE (a->ref))
|| wi::leu_p (MAX_DISTANCE, a->offset - last_ofs))
{
else
release_chain (chain);
- if (DR_IS_READ (a->ref))
- type = CT_LOAD;
- else
- type = comp->eliminate_store_p ? CT_STORE_STORE : CT_STORE_LOAD;
-
+ /* Determine type of the chain. If the root reference is a load,
+ this can only be a CT_LOAD chain; other chains are intialized
+ to CT_STORE_LOAD and might be promoted to CT_STORE_STORE when
+ new reference is added. */
+ type = DR_IS_READ (a->ref) ? CT_LOAD : CT_STORE_LOAD;
chain = make_rooted_chain (a, type);
last_ofs = a->offset;
continue;
values. */
for (unsigned i = 0; i < chain->length; i++)
{
- dref a = get_chain_last_ref_at (chain, i);
+ dref a = get_chain_last_write_at (chain, i);
if (a == NULL)
continue;
/* Initialize root value for eliminated stores at each distance. */
for (i = 0; i < n; i++)
{
- dref a = get_chain_last_ref_at (chain, i);
+ dref a = get_chain_last_write_at (chain, i);
if (a == NULL)
continue;
{
/* Root value is rhs operand of the store to be eliminated if
it isn't loaded from memory before loop. */
- dref a = get_chain_last_ref_at (chain, i);
+ dref a = get_chain_last_write_at (chain, i);
val = gimple_assign_rhs1 (a->stmt);
if (TREE_CLOBBER_P (val))
- val = get_or_create_ssa_default_def (cfun, SSA_NAME_VAR (var));
+ {
+ val = get_or_create_ssa_default_def (cfun, SSA_NAME_VAR (var));
+ gimple_assign_set_rhs1 (a->stmt, val);
+ }
vtemps[n - i - 1] = val;
}
execute_pred_commoning_chain (struct loop *loop, chain_p chain,
bitmap tmp_vars)
{
- unsigned i, n;
+ unsigned i;
dref a;
tree var;
bool in_lhs;
finalize_eliminated_stores (loop, chain);
}
- /* Eliminate the stores killed by following store. */
- n = chain->refs.length ();
- for (i = 0; i < n - 1; i++)
- remove_stmt (chain->refs[i]->stmt);
+ bool last_store_p = true;
+ for (i = chain->refs.length (); i > 0; i--)
+ {
+ a = chain->refs[i - 1];
+ /* Preserve the last store of the chain. Eliminate other stores
+ which are killed by the last one. */
+ if (DR_IS_WRITE (a->ref))
+ {
+ if (last_store_p)
+ last_store_p = false;
+ else
+ remove_stmt (a->stmt);
+
+ continue;
+ }
+
+ /* Any load in Store-Store chain must be dominated by a previous
+ store, we replace the load reference with rhs of the store. */
+ dref b = get_chain_last_write_before_load (chain, i - 1);
+ gcc_assert (b != NULL);
+ var = gimple_assign_rhs1 (b->stmt);
+ replace_ref_with (a->stmt, var, false, false);
+ }
}
else
{