re PR tree-optimization/52558 (write introduction incorrect wrt the C++11 memory model)
PR tree-optimization/52558 * cfg.c (alloc_aux_for_edge): Fix comment. (alloc_aux_for_edge): Remove static. * basic-block.h (alloc_aux_for_edge): Protoize. * tree-ssa-loop-im.c (execute_sm_if_changed): New. (execute_sm_if_changed_flag): New. (execute_sm_if_changed_flag_set): New. (execute_sm): Do not generate data races unless requested. (tree_ssa_lim_initialize): Call alloc_aux_for_edges. (tree_ssa_lim_finalize): Call free_aux_for_edges. * gimple.h (block_in_transaction): New. (gimple_in_transaction): Use block_in_transaction. From-SVN: r188081
This commit is contained in:
parent
19af62dbf2
commit
039496da00
@ -1,3 +1,18 @@
|
|||||||
|
2012-05-31 Aldy Hernandez <aldyh@redhat.com>
|
||||||
|
|
||||||
|
PR tree-optimization/52558
|
||||||
|
* cfg.c (alloc_aux_for_edge): Fix comment.
|
||||||
|
(alloc_aux_for_edge): Remove static.
|
||||||
|
* basic-block.h (alloc_aux_for_edge): Protoize.
|
||||||
|
* tree-ssa-loop-im.c (execute_sm_if_changed): New.
|
||||||
|
(execute_sm_if_changed_flag): New.
|
||||||
|
(execute_sm_if_changed_flag_set): New.
|
||||||
|
(execute_sm): Do not generate data races unless requested.
|
||||||
|
(tree_ssa_lim_initialize): Call alloc_aux_for_edges.
|
||||||
|
(tree_ssa_lim_finalize): Call free_aux_for_edges.
|
||||||
|
* gimple.h (block_in_transaction): New.
|
||||||
|
(gimple_in_transaction): Use block_in_transaction.
|
||||||
|
|
||||||
2012-05-31 Georg-Johann Lay <avr@gjlay.de>
|
2012-05-31 Georg-Johann Lay <avr@gjlay.de>
|
||||||
|
|
||||||
PR target/51345
|
PR target/51345
|
||||||
|
@ -802,6 +802,7 @@ extern basic_block alloc_block (void);
|
|||||||
extern void alloc_aux_for_blocks (int);
|
extern void alloc_aux_for_blocks (int);
|
||||||
extern void clear_aux_for_blocks (void);
|
extern void clear_aux_for_blocks (void);
|
||||||
extern void free_aux_for_blocks (void);
|
extern void free_aux_for_blocks (void);
|
||||||
|
extern void alloc_aux_for_edge (edge, int);
|
||||||
extern void alloc_aux_for_edges (int);
|
extern void alloc_aux_for_edges (int);
|
||||||
extern void clear_aux_for_edges (void);
|
extern void clear_aux_for_edges (void);
|
||||||
extern void free_aux_for_edges (void);
|
extern void free_aux_for_edges (void);
|
||||||
|
@ -814,10 +814,10 @@ free_aux_for_blocks (void)
|
|||||||
clear_aux_for_blocks ();
|
clear_aux_for_blocks ();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Allocate a memory edge of SIZE as BB->aux. The obstack must
|
/* Allocate a memory edge of SIZE as E->aux. The obstack must
|
||||||
be first initialized by alloc_aux_for_edges. */
|
be first initialized by alloc_aux_for_edges. */
|
||||||
|
|
||||||
static void
|
void
|
||||||
alloc_aux_for_edge (edge e, int size)
|
alloc_aux_for_edge (edge e, int size)
|
||||||
{
|
{
|
||||||
/* Verify that aux field is clear. */
|
/* Verify that aux field is clear. */
|
||||||
|
10
gcc/gimple.h
10
gcc/gimple.h
@ -1588,12 +1588,20 @@ gimple_set_has_volatile_ops (gimple stmt, bool volatilep)
|
|||||||
stmt->gsbase.has_volatile_ops = (unsigned) volatilep;
|
stmt->gsbase.has_volatile_ops = (unsigned) volatilep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Return true if BB is in a transaction. */
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
block_in_transaction (basic_block bb)
|
||||||
|
{
|
||||||
|
return bb->flags & BB_IN_TRANSACTION;
|
||||||
|
}
|
||||||
|
|
||||||
/* Return true if STMT is in a transaction. */
|
/* Return true if STMT is in a transaction. */
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
gimple_in_transaction (gimple stmt)
|
gimple_in_transaction (gimple stmt)
|
||||||
{
|
{
|
||||||
return gimple_bb (stmt)->flags & BB_IN_TRANSACTION;
|
return block_in_transaction (gimple_bb (stmt));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Return true if statement STMT may access memory. */
|
/* Return true if statement STMT may access memory. */
|
||||||
|
22
gcc/testsuite/gcc.dg/pr52558-1.c
Normal file
22
gcc/testsuite/gcc.dg/pr52558-1.c
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* { dg-do compile } */
|
||||||
|
/* { dg-options "--param allow-store-data-races=0 -O2 -fdump-tree-lim1" } */
|
||||||
|
|
||||||
|
/* Test that `count' is not written to unless p->data > 0. */
|
||||||
|
|
||||||
|
int count;
|
||||||
|
|
||||||
|
struct obj {
|
||||||
|
int data;
|
||||||
|
struct obj *next;
|
||||||
|
} *q;
|
||||||
|
|
||||||
|
void func()
|
||||||
|
{
|
||||||
|
struct obj *p;
|
||||||
|
for (p = q; p; p = p->next)
|
||||||
|
if (p->data > 0)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* { dg-final { scan-tree-dump-times "MEM count_lsm.. count_lsm_flag" 1 "lim1" } } */
|
||||||
|
/* { dg-final { cleanup-tree-dump "lim1" } } */
|
23
gcc/testsuite/gcc.dg/pr52558-2.c
Normal file
23
gcc/testsuite/gcc.dg/pr52558-2.c
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
/* { dg-do compile } */
|
||||||
|
/* { dg-options "--param allow-store-data-races=0 -O2 -fdump-tree-lim1" } */
|
||||||
|
|
||||||
|
/* Test that g_2 is not written to unless !g_1. */
|
||||||
|
|
||||||
|
int g_1 = 1;
|
||||||
|
int g_2 = 0;
|
||||||
|
|
||||||
|
int func_1(void)
|
||||||
|
{
|
||||||
|
int l;
|
||||||
|
for (l = 0; l < 1234; l++)
|
||||||
|
{
|
||||||
|
if (g_1)
|
||||||
|
return l;
|
||||||
|
else
|
||||||
|
g_2 = 0;
|
||||||
|
}
|
||||||
|
return 999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* { dg-final { scan-tree-dump-times "MEM.*g_2_lsm_flag" 1 "lim1" } } */
|
||||||
|
/* { dg-final { cleanup-tree-dump "lim1" } } */
|
24
gcc/testsuite/gcc.dg/tm/reg-promotion.c
Normal file
24
gcc/testsuite/gcc.dg/tm/reg-promotion.c
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/* { dg-do compile } */
|
||||||
|
/* { dg-options "-fgnu-tm -O2 -fdump-tree-lim1" } */
|
||||||
|
|
||||||
|
/* Test that `count' is not written to unless p->data>0. */
|
||||||
|
|
||||||
|
int count;
|
||||||
|
|
||||||
|
struct obj {
|
||||||
|
int data;
|
||||||
|
struct obj *next;
|
||||||
|
} *q;
|
||||||
|
|
||||||
|
void func()
|
||||||
|
{
|
||||||
|
struct obj *p;
|
||||||
|
__transaction_atomic {
|
||||||
|
for (p = q; p; p = p->next)
|
||||||
|
if (p->data > 0)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* { dg-final { scan-tree-dump-times "MEM count_lsm.. count_lsm_flag" 1 "lim1" } } */
|
||||||
|
/* { dg-final { cleanup-tree-dump "lim1" } } */
|
@ -52,7 +52,7 @@ along with GCC; see the file COPYING3. If not see
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Where COND and INV are is invariants, but evaluating INV may trap or be
|
Where COND and INV are invariants, but evaluating INV may trap or be
|
||||||
invalid from some other reason if !COND. This may be transformed to
|
invalid from some other reason if !COND. This may be transformed to
|
||||||
|
|
||||||
if (cond)
|
if (cond)
|
||||||
@ -1626,6 +1626,7 @@ gather_mem_refs_stmt (struct loop *loop, gimple stmt)
|
|||||||
fprintf (dump_file, "\n");
|
fprintf (dump_file, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_stored)
|
if (is_stored)
|
||||||
mark_ref_stored (ref, loop);
|
mark_ref_stored (ref, loop);
|
||||||
|
|
||||||
@ -1956,6 +1957,173 @@ get_lsm_tmp_name (tree ref, unsigned n)
|
|||||||
return lsm_tmp_name;
|
return lsm_tmp_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct prev_flag_edges {
|
||||||
|
/* Edge to insert new flag comparison code. */
|
||||||
|
edge append_cond_position;
|
||||||
|
|
||||||
|
/* Edge for fall through from previous flag comparison. */
|
||||||
|
edge last_cond_fallthru;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Helper function for execute_sm. Emit code to store TMP_VAR into
|
||||||
|
MEM along edge EX.
|
||||||
|
|
||||||
|
The store is only done if MEM has changed. We do this so no
|
||||||
|
changes to MEM occur on code paths that did not originally store
|
||||||
|
into it.
|
||||||
|
|
||||||
|
The common case for execute_sm will transform:
|
||||||
|
|
||||||
|
for (...) {
|
||||||
|
if (foo)
|
||||||
|
stuff;
|
||||||
|
else
|
||||||
|
MEM = TMP_VAR;
|
||||||
|
}
|
||||||
|
|
||||||
|
into:
|
||||||
|
|
||||||
|
lsm = MEM;
|
||||||
|
for (...) {
|
||||||
|
if (foo)
|
||||||
|
stuff;
|
||||||
|
else
|
||||||
|
lsm = TMP_VAR;
|
||||||
|
}
|
||||||
|
MEM = lsm;
|
||||||
|
|
||||||
|
This function will generate:
|
||||||
|
|
||||||
|
lsm = MEM;
|
||||||
|
|
||||||
|
lsm_flag = false;
|
||||||
|
...
|
||||||
|
for (...) {
|
||||||
|
if (foo)
|
||||||
|
stuff;
|
||||||
|
else {
|
||||||
|
lsm = TMP_VAR;
|
||||||
|
lsm_flag = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (lsm_flag) <--
|
||||||
|
MEM = lsm; <--
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void
|
||||||
|
execute_sm_if_changed (edge ex, tree mem, tree tmp_var, tree flag)
|
||||||
|
{
|
||||||
|
basic_block new_bb, then_bb, old_dest;
|
||||||
|
bool loop_has_only_one_exit;
|
||||||
|
edge then_old_edge, orig_ex = ex;
|
||||||
|
gimple_stmt_iterator gsi;
|
||||||
|
gimple stmt;
|
||||||
|
struct prev_flag_edges *prev_edges = (struct prev_flag_edges *) ex->aux;
|
||||||
|
|
||||||
|
/* ?? Insert store after previous store if applicable. See note
|
||||||
|
below. */
|
||||||
|
if (prev_edges)
|
||||||
|
ex = prev_edges->append_cond_position;
|
||||||
|
|
||||||
|
loop_has_only_one_exit = single_pred_p (ex->dest);
|
||||||
|
|
||||||
|
if (loop_has_only_one_exit)
|
||||||
|
ex = split_block_after_labels (ex->dest);
|
||||||
|
|
||||||
|
old_dest = ex->dest;
|
||||||
|
new_bb = split_edge (ex);
|
||||||
|
then_bb = create_empty_bb (new_bb);
|
||||||
|
if (current_loops && new_bb->loop_father)
|
||||||
|
add_bb_to_loop (then_bb, new_bb->loop_father);
|
||||||
|
|
||||||
|
gsi = gsi_start_bb (new_bb);
|
||||||
|
stmt = gimple_build_cond (NE_EXPR, flag, boolean_false_node,
|
||||||
|
NULL_TREE, NULL_TREE);
|
||||||
|
gsi_insert_after (&gsi, stmt, GSI_CONTINUE_LINKING);
|
||||||
|
|
||||||
|
gsi = gsi_start_bb (then_bb);
|
||||||
|
/* Insert actual store. */
|
||||||
|
stmt = gimple_build_assign (unshare_expr (mem), tmp_var);
|
||||||
|
gsi_insert_after (&gsi, stmt, GSI_CONTINUE_LINKING);
|
||||||
|
|
||||||
|
make_edge (new_bb, then_bb, EDGE_TRUE_VALUE);
|
||||||
|
make_edge (new_bb, old_dest, EDGE_FALSE_VALUE);
|
||||||
|
then_old_edge = make_edge (then_bb, old_dest, EDGE_FALLTHRU);
|
||||||
|
|
||||||
|
set_immediate_dominator (CDI_DOMINATORS, then_bb, new_bb);
|
||||||
|
|
||||||
|
if (prev_edges)
|
||||||
|
{
|
||||||
|
basic_block prevbb = prev_edges->last_cond_fallthru->src;
|
||||||
|
redirect_edge_succ (prev_edges->last_cond_fallthru, new_bb);
|
||||||
|
set_immediate_dominator (CDI_DOMINATORS, new_bb, prevbb);
|
||||||
|
set_immediate_dominator (CDI_DOMINATORS, old_dest,
|
||||||
|
recompute_dominator (CDI_DOMINATORS, old_dest));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ?? Because stores may alias, they must happen in the exact
|
||||||
|
sequence they originally happened. Save the position right after
|
||||||
|
the (_lsm) store we just created so we can continue appending after
|
||||||
|
it and maintain the original order. */
|
||||||
|
{
|
||||||
|
struct prev_flag_edges *p;
|
||||||
|
|
||||||
|
if (orig_ex->aux)
|
||||||
|
orig_ex->aux = NULL;
|
||||||
|
alloc_aux_for_edge (orig_ex, sizeof (struct prev_flag_edges));
|
||||||
|
p = (struct prev_flag_edges *) orig_ex->aux;
|
||||||
|
p->append_cond_position = then_old_edge;
|
||||||
|
p->last_cond_fallthru = find_edge (new_bb, old_dest);
|
||||||
|
orig_ex->aux = (void *) p;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!loop_has_only_one_exit)
|
||||||
|
for (gsi = gsi_start_phis (old_dest); !gsi_end_p (gsi); gsi_next (&gsi))
|
||||||
|
{
|
||||||
|
gimple phi = gsi_stmt (gsi);
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
for (i = 0; i < gimple_phi_num_args (phi); i++)
|
||||||
|
if (gimple_phi_arg_edge (phi, i)->src == new_bb)
|
||||||
|
{
|
||||||
|
tree arg = gimple_phi_arg_def (phi, i);
|
||||||
|
add_phi_arg (phi, arg, then_old_edge, UNKNOWN_LOCATION);
|
||||||
|
update_stmt (phi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Remove the original fall through edge. This was the
|
||||||
|
single_succ_edge (new_bb). */
|
||||||
|
EDGE_SUCC (new_bb, 0)->flags &= ~EDGE_FALLTHRU;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper function for execute_sm. On every location where REF is
|
||||||
|
set, set an appropriate flag indicating the store. */
|
||||||
|
|
||||||
|
static tree
|
||||||
|
execute_sm_if_changed_flag_set (struct loop *loop, mem_ref_p ref)
|
||||||
|
{
|
||||||
|
unsigned i;
|
||||||
|
mem_ref_loc_p loc;
|
||||||
|
tree flag;
|
||||||
|
VEC (mem_ref_loc_p, heap) *locs = NULL;
|
||||||
|
char *str = get_lsm_tmp_name (ref->mem, ~0);
|
||||||
|
|
||||||
|
lsm_tmp_name_add ("_flag");
|
||||||
|
flag = make_rename_temp (boolean_type_node, str);
|
||||||
|
get_all_locs_in_loop (loop, ref, &locs);
|
||||||
|
FOR_EACH_VEC_ELT (mem_ref_loc_p, locs, i, loc)
|
||||||
|
{
|
||||||
|
gimple_stmt_iterator gsi;
|
||||||
|
gimple stmt;
|
||||||
|
|
||||||
|
gsi = gsi_for_stmt (loc->stmt);
|
||||||
|
stmt = gimple_build_assign (flag, boolean_true_node);
|
||||||
|
gsi_insert_after (&gsi, stmt, GSI_CONTINUE_LINKING);
|
||||||
|
}
|
||||||
|
VEC_free (mem_ref_loc_p, heap, locs);
|
||||||
|
return flag;
|
||||||
|
}
|
||||||
|
|
||||||
/* Executes store motion of memory reference REF from LOOP.
|
/* Executes store motion of memory reference REF from LOOP.
|
||||||
Exits from the LOOP are stored in EXITS. The initialization of the
|
Exits from the LOOP are stored in EXITS. The initialization of the
|
||||||
temporary variable is put to the preheader of the loop, and assignments
|
temporary variable is put to the preheader of the loop, and assignments
|
||||||
@ -1964,12 +2132,13 @@ get_lsm_tmp_name (tree ref, unsigned n)
|
|||||||
static void
|
static void
|
||||||
execute_sm (struct loop *loop, VEC (edge, heap) *exits, mem_ref_p ref)
|
execute_sm (struct loop *loop, VEC (edge, heap) *exits, mem_ref_p ref)
|
||||||
{
|
{
|
||||||
tree tmp_var;
|
tree tmp_var, store_flag;
|
||||||
unsigned i;
|
unsigned i;
|
||||||
gimple load, store;
|
gimple load;
|
||||||
struct fmt_data fmt_data;
|
struct fmt_data fmt_data;
|
||||||
edge ex;
|
edge ex, latch_edge;
|
||||||
struct lim_aux_data *lim_data;
|
struct lim_aux_data *lim_data;
|
||||||
|
bool multi_threaded_model_p = false;
|
||||||
|
|
||||||
if (dump_file && (dump_flags & TDF_DETAILS))
|
if (dump_file && (dump_flags & TDF_DETAILS))
|
||||||
{
|
{
|
||||||
@ -1985,23 +2154,47 @@ execute_sm (struct loop *loop, VEC (edge, heap) *exits, mem_ref_p ref)
|
|||||||
fmt_data.orig_loop = loop;
|
fmt_data.orig_loop = loop;
|
||||||
for_each_index (&ref->mem, force_move_till, &fmt_data);
|
for_each_index (&ref->mem, force_move_till, &fmt_data);
|
||||||
|
|
||||||
|
if ((flag_tm && block_in_transaction (loop_preheader_edge (loop)->src))
|
||||||
|
|| !PARAM_VALUE (PARAM_ALLOW_STORE_DATA_RACES))
|
||||||
|
multi_threaded_model_p = true;
|
||||||
|
|
||||||
|
if (multi_threaded_model_p)
|
||||||
|
store_flag = execute_sm_if_changed_flag_set (loop, ref);
|
||||||
|
|
||||||
rewrite_mem_refs (loop, ref, tmp_var);
|
rewrite_mem_refs (loop, ref, tmp_var);
|
||||||
|
|
||||||
/* Emit the load & stores. */
|
/* Emit the load code into the latch, so that we are sure it will
|
||||||
|
be processed after all dependencies. */
|
||||||
|
latch_edge = loop_latch_edge (loop);
|
||||||
|
|
||||||
|
/* FIXME/TODO: For the multi-threaded variant, we could avoid this
|
||||||
|
load altogether, since the store is predicated by a flag. We
|
||||||
|
could, do the load only if it was originally in the loop. */
|
||||||
load = gimple_build_assign (tmp_var, unshare_expr (ref->mem));
|
load = gimple_build_assign (tmp_var, unshare_expr (ref->mem));
|
||||||
lim_data = init_lim_data (load);
|
lim_data = init_lim_data (load);
|
||||||
lim_data->max_loop = loop;
|
lim_data->max_loop = loop;
|
||||||
lim_data->tgt_loop = loop;
|
lim_data->tgt_loop = loop;
|
||||||
|
gsi_insert_on_edge (latch_edge, load);
|
||||||
|
|
||||||
/* Put this into the latch, so that we are sure it will be processed after
|
if (multi_threaded_model_p)
|
||||||
all dependencies. */
|
|
||||||
gsi_insert_on_edge (loop_latch_edge (loop), load);
|
|
||||||
|
|
||||||
FOR_EACH_VEC_ELT (edge, exits, i, ex)
|
|
||||||
{
|
{
|
||||||
store = gimple_build_assign (unshare_expr (ref->mem), tmp_var);
|
load = gimple_build_assign (store_flag, boolean_false_node);
|
||||||
gsi_insert_on_edge (ex, store);
|
lim_data = init_lim_data (load);
|
||||||
|
lim_data->max_loop = loop;
|
||||||
|
lim_data->tgt_loop = loop;
|
||||||
|
gsi_insert_on_edge (latch_edge, load);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Sink the store to every exit from the loop. */
|
||||||
|
FOR_EACH_VEC_ELT (edge, exits, i, ex)
|
||||||
|
if (!multi_threaded_model_p)
|
||||||
|
{
|
||||||
|
gimple store;
|
||||||
|
store = gimple_build_assign (unshare_expr (ref->mem), tmp_var);
|
||||||
|
gsi_insert_on_edge (ex, store);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
execute_sm_if_changed (ex, ref->mem, tmp_var, store_flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Hoists memory references MEM_REFS out of LOOP. EXITS is the list of exit
|
/* Hoists memory references MEM_REFS out of LOOP. EXITS is the list of exit
|
||||||
@ -2410,6 +2603,8 @@ tree_ssa_lim_initialize (void)
|
|||||||
|
|
||||||
if (flag_tm)
|
if (flag_tm)
|
||||||
compute_transaction_bits ();
|
compute_transaction_bits ();
|
||||||
|
|
||||||
|
alloc_aux_for_edges (0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Cleans up after the invariant motion pass. */
|
/* Cleans up after the invariant motion pass. */
|
||||||
@ -2421,6 +2616,8 @@ tree_ssa_lim_finalize (void)
|
|||||||
unsigned i;
|
unsigned i;
|
||||||
bitmap b;
|
bitmap b;
|
||||||
|
|
||||||
|
free_aux_for_edges ();
|
||||||
|
|
||||||
FOR_EACH_BB (bb)
|
FOR_EACH_BB (bb)
|
||||||
SET_ALWAYS_EXECUTED_IN (bb, NULL);
|
SET_ALWAYS_EXECUTED_IN (bb, NULL);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user