===================================================================
@@ -135,6 +135,47 @@
*/
+/* Helper function for bb_in_transaction. Returns true if the first
+ statement in BB is in a transaction. If the BB does not have any
+ statements, return -1. */
+
+static int
+bb_in_transaction_1 (basic_block bb)
+{
+ gimple t;
+
+ t = gimple_seq_first_stmt (bb_seq (bb));
+ if (t)
+ return gimple_in_transaction (t);
+ return -1;
+}
+
+/* Return true if BB is in a transaction. This can only be called
+ after transaction bits have been calculated with
+ compute_transaction_bits(). */
+
+bool
+bb_in_transaction (basic_block bb)
+{
+ edge e;
+ edge_iterator ei;
+ int res;
+
+ res = bb_in_transaction_1 (bb);
+ if (res != -1)
+ return (bool) res;
+
+ /* ?? Perhaps we should cache this somewhere in the BB, or are
+ multiple levels of empty BB's common. ?? */
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ {
+ int res = bb_in_transaction_1 (e->src);
+ if (res != -1)
+ return (bool) res;
+ }
+ return false;
+}
+
/* Return the attributes we want to examine for X, or NULL if it's not
something we examine. We look at function types, but allow pointers
to function types and function decls and peek through. */
===================================================================
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.
#include "tree-affine.h"
#include "pointer-set.h"
#include "tree-ssa-propagate.h"
+#include "target.h"
/* TODO: Support for predicated code motion. I.e.
@@ -52,7 +53,7 @@ along with GCC; see the file COPYING3.
}
}
- 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
if (cond)
@@ -1956,6 +1957,161 @@ get_lsm_tmp_name (tree ref, unsigned n)
return lsm_tmp_name;
}
+/* 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 either generate when FLAG is present:
+
+ lsm = MEM;
+ flag = false;
+ ...
+ if (foo)
+ stuff;
+ else {
+ lsm = TMP_VAR;
+ flag = true;
+ }
+ if (flag) <--
+ MEM = lsm; <--
+
+ or the following when FLAG is not set:
+
+ lsm = MEM;
+ for (...) {
+ if (foo)
+ stuff;
+ else
+ lsm = TMP_VAR;
+ }
+ if (lsm != MEM) <--
+ MEM = lsm; <--
+*/
+
+static void
+execute_sm_if_changed (edge ex, tree mem, tree orig_mem_ssa, tree tmp_var,
+ tree flag)
+{
+ basic_block new_bb, then_bb, old_dest = ex->dest;
+ bool single_exit_p = single_pred_p (ex->dest);
+ edge then_old_edge;
+ gimple_stmt_iterator gsi;
+ gimple stmt;
+
+ if (single_exit_p)
+ ex = split_block_after_labels (old_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);
+ if (flag)
+ stmt = gimple_build_cond (NE_EXPR, flag, boolean_false_node,
+ NULL_TREE, NULL_TREE);
+ else
+ stmt = gimple_build_cond (NE_EXPR, orig_mem_ssa, tmp_var,
+ 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 (!single_exit_p)
+ 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 path that sets REF, 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_start_bb (gimple_bb (loc->stmt));
+ for (; !gsi_end_p (gsi); gsi_next (&gsi))
+ if (gsi_stmt (gsi) == loc->stmt)
+ {
+ stmt = gimple_build_assign (flag, boolean_true_node);
+ gsi_insert_after (&gsi, stmt, GSI_CONTINUE_LINKING);
+ break;
+ }
+ }
+ VEC_free (mem_ref_loc_p, heap, locs);
+ return flag;
+}
+
+/* Store motion types for execute_sm below. */
+enum store_type
+ {
+ /* Traditional single-thread. */
+ STORE_SINGLE_THREAD,
+ /* Store only if value changed. */
+ STORE_IF_CHANGED,
+ /* Store only if flag changed. */
+ STORE_IF_CHANGED_FLAG
+ };
+
/* Executes store motion of memory reference REF from LOOP.
Exits from the LOOP are stored in EXITS. The initialization of the
temporary variable is put to the preheader of the loop, and assignments
@@ -1964,12 +2120,13 @@ get_lsm_tmp_name (tree ref, unsigned n)
static void
execute_sm (struct loop *loop, VEC (edge, heap) *exits, mem_ref_p ref)
{
- tree tmp_var;
+ tree tmp_var, mem_ssa, store_flag;
unsigned i;
- gimple load, store;
+ gimple load;
struct fmt_data fmt_data;
- edge ex;
+ edge ex, latch_edge;
struct lim_aux_data *lim_data;
+ enum store_type store;
if (dump_file && (dump_flags & TDF_DETAILS))
{
@@ -1978,6 +2135,8 @@ execute_sm (struct loop *loop, VEC (edge
fprintf (dump_file, " from loop %d\n", loop->num);
}
+ mem_ssa = create_tmp_reg (TREE_TYPE (unshare_expr (ref->mem)), NULL);
+ mem_ssa = make_ssa_name (mem_ssa, NULL);
tmp_var = make_rename_temp (TREE_TYPE (ref->mem),
get_lsm_tmp_name (ref->mem, ~0));
@@ -1985,22 +2144,66 @@ execute_sm (struct loop *loop, VEC (edge
fmt_data.orig_loop = loop;
for_each_index (&ref->mem, force_move_till, &fmt_data);
+ if ((flag_tm && bb_in_transaction (loop_preheader_edge (loop)->src))
+ || !PARAM_VALUE (PARAM_ALLOW_STORE_DATA_RACES))
+ {
+ if (targetm.can_compare_bitwise_p (TYPE_MODE (TREE_TYPE (ref->mem))))
+ store = STORE_IF_CHANGED;
+ else
+ store = STORE_IF_CHANGED_FLAG;
+ }
+ else
+ store = STORE_SINGLE_THREAD;
+
+#if 1
+ /* ?? testing ?? */
+ store = STORE_IF_CHANGED_FLAG;
+#endif
+
+ if (store == STORE_IF_CHANGED_FLAG)
+ store_flag = execute_sm_if_changed_flag_set (loop, ref);
+
rewrite_mem_refs (loop, ref, tmp_var);
- /* Emit the load & stores. */
- load = gimple_build_assign (tmp_var, unshare_expr (ref->mem));
+
+ /* 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);
+ load = gimple_build_assign (mem_ssa, unshare_expr (ref->mem));
lim_data = init_lim_data (load);
lim_data->max_loop = loop;
lim_data->tgt_loop = loop;
+ gsi_insert_on_edge (latch_edge, load);
+ load = gimple_build_assign (tmp_var, mem_ssa);
+ lim_data = init_lim_data (load);
+ lim_data->max_loop = loop;
+ lim_data->tgt_loop = loop;
+ gsi_insert_on_edge (latch_edge, load);
+ if (store == STORE_IF_CHANGED_FLAG)
+ {
+ load = gimple_build_assign (store_flag, boolean_false_node);
+ lim_data = init_lim_data (load);
+ lim_data->max_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
- all dependencies. */
- gsi_insert_on_edge (loop_latch_edge (loop), load);
-
+ /* Sink the store to every exit from the loop. */
FOR_EACH_VEC_ELT (edge, exits, i, ex)
{
- store = gimple_build_assign (unshare_expr (ref->mem), tmp_var);
- gsi_insert_on_edge (ex, store);
+ if (store == STORE_SINGLE_THREAD)
+ {
+ gimple store;
+ store = gimple_build_assign (unshare_expr (ref->mem), tmp_var);
+ gsi_insert_on_edge (ex, store);
+ }
+ else if (store == STORE_IF_CHANGED)
+ execute_sm_if_changed (ex, ref->mem, mem_ssa, tmp_var, NULL);
+ else if (store == STORE_IF_CHANGED_FLAG)
+ execute_sm_if_changed (ex, ref->mem, mem_ssa, tmp_var,
+ store_flag);
+ else
+ gcc_unreachable ();
}
}
===================================================================
@@ -2667,6 +2667,13 @@ DEFHOOK
enum unwind_info_type, (void),
default_debug_unwind_info)
+DEFHOOK
+(can_compare_bitwise_p,
+ "This hook should return true if the target can efficiently compare two\
+ registers in the given mode for bit equality.",
+ bool, (enum machine_mode),
+ default_can_compare_bitwise_p)
+
DEFHOOKPOD
(atomic_test_and_set_trueval,
"This value should be set if the result written by\
===================================================================
@@ -1122,6 +1122,7 @@ extern tree omp_reduction_init (tree, tr
/* In trans-mem.c. */
extern void diagnose_tm_safe_errors (tree);
extern void compute_transaction_bits (void);
+extern bool bb_in_transaction (basic_block);
/* In tree-nested.c. */
extern void lower_nested_functions (tree);
===================================================================
@@ -2532,7 +2532,7 @@ tree-ssa-loop-im.o : tree-ssa-loop-im.c
$(PARAMS_H) output.h $(DIAGNOSTIC_H) $(TIMEVAR_H) $(TM_H) coretypes.h \
$(TREE_DUMP_H) $(TREE_PASS_H) $(FLAGS_H) $(BASIC_BLOCK_H) \
pointer-set.h tree-affine.h tree-ssa-propagate.h gimple-pretty-print.h \
- tree-pretty-print.h
+ tree-pretty-print.h $(TARGET_H)
tree-ssa-math-opts.o : tree-ssa-math-opts.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
$(TM_H) $(FLAGS_H) $(TREE_H) $(TREE_FLOW_H) $(TIMEVAR_H) \
$(TREE_PASS_H) alloc-pool.h $(BASIC_BLOCK_H) $(TARGET_H) \
===================================================================
@@ -9471,6 +9471,10 @@ how you define @code{DWARF2_FRAME_INFO}.
@end defmac
@deftypefn {Target Hook} {enum unwind_info_type} TARGET_DEBUG_UNWIND_INFO (void)
+
+@deftypefn {Target Hook} bool TARGET_CAN_COMPARE_BITWISE_P (enum @var{machine_mode})
+This hook should return true if the target can efficiently compare two registers in the given mode for bit equality.
+@end deftypefn
This hook defines the mechanism that will be used for describing frame
unwind information to the debugger. Normally the hook will return
@code{UI_DWARF2} if DWARF 2 debug information is enabled, and
===================================================================
@@ -1331,6 +1331,15 @@ default_debug_unwind_info (void)
return UI_NONE;
}
+/* This hook returns true if the target can efficiently compare
+ two registers in the given mode for bit equality. */
+
+bool
+default_can_compare_bitwise_p (enum machine_mode mode)
+{
+ return SCALAR_INT_MODE_P (mode);
+}
+
/* To be used by targets where reg_raw_mode doesn't return the right
mode for registers used in apply_builtin_return and apply_builtin_arg. */
===================================================================
@@ -168,6 +168,8 @@ extern unsigned char default_class_max_n
extern enum unwind_info_type default_debug_unwind_info (void);
+extern bool default_can_compare_bitwise_p (enum machine_mode);
+
extern int default_label_align_after_barrier_max_skip (rtx);
extern int default_loop_align_max_skip (rtx);
extern int default_label_align_max_skip (rtx);