On 11/06/11 12:20, Richard Henderson wrote:
- if (!computed_goto_p (stmt))
+ if (!computed_goto_p (stmt))
{
- tree new_dest = main_block_label (gimple_goto_dest (stmt));
- gimple_goto_set_dest (stmt, new_dest);
+ label = gimple_goto_dest (stmt);
+ new_label = main_block_label (label);
+ if (new_label != label)
+ gimple_goto_set_dest (stmt, new_label);
What's the reason for this changes? Optimization?
Yes. Rth can elaborate if you deem necessary.
Really? I have no idea what this change achieves.
I actually wonder if this is a merge error.
Removing this caused various TM tests failures, which I have yet to
fully investigate. I found the original patch by you [rth] (attached).
Perhaps you can elaborate as to its original use.
It may be that we need to remove all of the GIMPLE_GOTO, GIMPLE_COND,
and GIMPLE_SWITCH hacks in cleanup_dead_labels, but I will wait for a
double check by you before touching any more of this.
In the meantime, I will commit the patch sans this GIMPLE_GOTO removal
which may still be used. That is, after another round of tests.
Index: cgraphbuild.c
===================================================================
--- cgraphbuild.c (revision 141199)
+++ cgraphbuild.c (revision 141200)
@@ -125,7 +125,7 @@ compute_call_stmt_bb_frequency (basic_bl
/* Eagerly clone functions so that TM expansion can create
and redirect calls to a transactional clone. */
-static void
+static void ATTRIBUTE_UNUSED
prepare_tm_clone (struct cgraph_node *node)
{
struct cgraph_node *tm_node;
@@ -275,7 +275,7 @@ build_cgraph_edges (void)
build_cgraph_edges_from_node (node);
- prepare_tm_clone (node);
+ /* prepare_tm_clone (node); */
return 0;
}
Index: tree-pass.h
===================================================================
--- tree-pass.h (revision 141199)
+++ tree-pass.h (revision 141200)
@@ -388,7 +388,7 @@ extern struct gimple_opt_pass pass_reass
extern struct gimple_opt_pass pass_rebuild_cgraph_edges;
extern struct gimple_opt_pass pass_build_cgraph_edges;
extern struct gimple_opt_pass pass_reset_cc_flags;
-extern struct gimple_opt_pass pass_expand_tm;
+extern struct gimple_opt_pass pass_lower_tm;
extern struct gimple_opt_pass pass_checkpoint_tm;
/* IPA Passes */
Index: gtm-low.c
===================================================================
--- gtm-low.c (revision 141199)
+++ gtm-low.c (revision 141200)
@@ -1,6 +1,6 @@
/* Lowering pass for transactional memory directives.
Converts markers of transactions into explicit calls to
- the STM runtime library.
+ the TM runtime library.
Copyright (C) 2008 Free Software Foundation, Inc.
@@ -18,34 +18,144 @@
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
- <http://www.gnu.org/licenses/>.
-
-*/
+ <http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "coretypes.h"
#include "tm.h"
#include "tree.h"
-#include "rtl.h"
#include "gimple.h"
-#include "langhooks.h"
-#include "diagnostic.h"
#include "tree-flow.h"
-#include "timevar.h"
-#include "flags.h"
-#include "function.h"
-#include "expr.h"
-#include "toplev.h"
#include "tree-pass.h"
-#include "ggc.h"
#include "except.h"
-#include "splay-tree.h"
-#include "optabs.h"
-#include "cfgloop.h"
-#include "tree-ssa-live.h"
-#include "tree-flow.h"
+#include "diagnostic.h"
+
+
+/* The representation of a transaction changes several times during the
+ lowering process. In the beginning, in the front-end we have the
+ GENERIC tree TM_ATOMIC. For example,
+
+ __tm_atomic {
+ local++;
+ if (++global == 10)
+ __tm_abort;
+ }
+
+ is represented as
+
+ TM_ATOMIC {
+ local++;
+ if (++global == 10)
+ __builtin___tm_abort ();
+ }
+
+ During initial gimplification (gimplify.c) the TM_ATOMIC node is
+ trivially replaced with a GIMPLE_TM_ATOMIC node, and we add bits
+ to handle EH cleanup of the transaction:
+
+ GIMPLE_TM_ATOMIC [label=NULL] {
+ try {
+ local = local + 1;
+ t0 [tm_load]= global;
+ t1 = t0 + 1;
+ global [tm_store]= t1;
+ if (t1 == 10)
+ __builtin___tm_abort ();
+ } finally {
+ __builtin___tm_commit ();
+ }
+ }
+
+ During pass_lower_eh, we create EH regions for the transactions,
+ intermixed with the regular EH stuff. This gives us a nice persistent
+ mapping (all the way through rtl) from transactional memory operation
+ back to the transaction, which allows us to get the abnormal edges
+ correct to model transaction aborts and restarts.
+
+ During pass_lower_tm, we mark the gimple statements that perform
+ transactional memory operations with TM_LOAD/TM_STORE, and swap out
+ function calls with their (non-)transactional clones. At this time
+ we flatten nested transactions (when possible), and flatten the
+ GIMPLE representation.
+
+ GIMPLE_TM_ATOMIC [label=over]
+ eh_label:
+ local = local + 1;
+ t0 [tm_load]= global;
+ t1 = t0 + 1;
+ global [tm_store]= t1;
+ if (t1 == 10)
+ __builtin___tm_abort ();
+ __builtin___tm_commit ();
+ over:
+
+ During pass_checkpoint_tm, we complete the lowering of the
+ GIMPLE_TM_ATOMIC node. Here we examine the SSA web and arange for
+ local variables to be saved and restored in the event of an abort.
+
+ save_local = local;
+ x = __builtin___tm_start (MAY_ABORT);
+ eh_label:
+ if (x & restore_locals) {
+ local = save_local;
+ }
+ if (x & abort_transaction)
+ goto over;
+ local = local + 1;
+ t0 [tm_load]= global;
+ t1 = t0 + 1;
+ global [tm_store]= t1;
+ if (t1 == 10)
+ __builtin___tm_abort ();
+ __builtin___tm_commit ();
+ over:
+
+ During expansion to rtl, we expand the TM_LOAD/TM_STORE markings
+ with calls to the appropriate builtin functions. Delaying this long
+ allows the tree optimizers the most visibility into the operations. */
+
+DEF_VEC_O(gimple_stmt_iterator);
+DEF_VEC_ALLOC_O(gimple_stmt_iterator,heap);
+
+struct ltm_state
+{
+ /* Bits to be stored in the GIMPLE_TM_ATOMIC subcode. */
+ unsigned subcode;
+
+ /* The EH region number for this transaction. Non-negative numbers
+ represent an active transaction within this function; -1 represents
+ an active transaction from a calling function (i.e. we're compiling
+ a transaction clone). For no active transaction, the state pointer
+ passed will be null. */
+ int region_nr;
+
+ /* Record the iterator pointing to a __tm_commit function call that
+ binds to this transaction region. There may be many such calls,
+ depending on how the EH expansion of the try-finally node went.
+ But there's usually exactly one such call, and essentially always
+ only a small number, so to avoid rescanning the entire sequence
+ when we need to remove these calls, record the iterator location. */
+ VEC(gimple_stmt_iterator,heap) *commit_stmts;
+};
+
+
+static void lower_sequence_tm (struct ltm_state *, gimple_seq);
+static void lower_sequence_no_tm (gimple_seq);
+
+/* Record the transaction for this statement. If the statement
+ already has a region number that's fine -- it means that the
+ statement can also throw. If there's no region number, it
+ means we're expanding a transactional clone and the region
+ is in a calling function. */
+
+static void
+add_stmt_to_transaction (struct ltm_state *state, gimple stmt)
+{
+ if (state->region_nr >= 0 && lookup_stmt_eh_region (stmt) < 0)
+ add_stmt_to_eh_region (stmt, state->region_nr);
+}
/* Determine whether X has to be instrumented using a read
or write barrier. */
@@ -78,31 +188,47 @@ requires_barrier (tree x)
}
}
-/* Subsituting a MODIFY_STMT with calls to the STM runtime. */
+/* Mark the GIMPLE_ASSIGN statement as appropriate for being inside
+ a transaction region. */
static void
-maybe_transactify_assign (gimple stmt)
+lower_assign_tm (struct ltm_state *state, gimple_stmt_iterator *gsi)
{
+ gimple stmt = gsi_stmt (*gsi);
bool load_p = requires_barrier (gimple_assign_rhs1 (stmt));
bool store_p = requires_barrier (gimple_assign_lhs (stmt));
- if (load_p)
+ if (load_p && store_p)
+ {
+ /* ??? This is a copy between two aggregates in memory. I
+ believe the Intel compiler handles this with a special
+ version of memcpy. For now, just consider the transaction
+ irrevokable at this point. */
+ state->subcode |= GTMA_HAVE_CALL_IRREVOKABLE;
+ return;
+ }
+ else if (load_p)
{
- gcc_assert (!store_p);
gimple_assign_set_rhs_code (stmt, TM_LOAD);
+ state->subcode |= GTMA_HAVE_LOAD;
}
else if (store_p)
- gimple_assign_set_rhs_code (stmt, TM_STORE);
+ {
+ gimple_assign_set_rhs_code (stmt, TM_STORE);
+ state->subcode |= GTMA_HAVE_STORE;
+ }
+ else
+ return;
+
+ add_stmt_to_transaction (state, stmt);
}
-/* Helper function that replaces call expressions inside
- transactions and issues a warning if no transactional
- clone is found. */
+/* Mark a GIMPLE_CALL as appropriate for being inside a transaction. */
static void
-maybe_transactify_call (gimple stmt)
+lower_call_tm (struct ltm_state *state, gimple_stmt_iterator *gsi)
{
- bool redirected = false;
+ gimple stmt = gsi_stmt (*gsi);
tree fn_decl;
struct cgraph_node *node, *orig_node;
int flags;
@@ -114,9 +240,28 @@ maybe_transactify_call (gimple stmt)
fn_decl = gimple_call_fndecl (stmt);
if (!fn_decl)
{
- warning (0, "Indirect call potentially breaks isolation of
transactions");
+ state->subcode |= GTMA_HAVE_CALL_INDIRECT;
return;
}
+
+ /* Check if this call is one of our transactional builtins. */
+ if (DECL_BUILT_IN_CLASS (fn_decl) == BUILT_IN_NORMAL)
+ switch (DECL_FUNCTION_CODE (fn_decl))
+ {
+ case BUILT_IN_TM_COMMIT:
+ /* Remember the commit so that we can remove it if
+ we decide to elide the transaction. */
+ VEC_safe_push (gimple_stmt_iterator,heap, state->commit_stmts, gsi);
+ return;
+ case BUILT_IN_TM_ABORT:
+ state->subcode |= GTMA_HAVE_ABORT;
+ add_stmt_to_transaction (state, stmt);
+ return;
+
+ default:
+ break;
+ }
+
if (DECL_IS_TM_PURE (fn_decl))
return;
@@ -129,12 +274,11 @@ maybe_transactify_call (gimple stmt)
if (DECL_IS_TM_CLONE (node->decl))
break;
}
-
if (DECL_IS_TM_CLONE (node->decl))
{
struct cgraph_edge *callers = orig_node->callers;
- /* Find appropriate call stmt to redirect */
+ /* Find appropriate call stmt to redirect. */
while (callers)
{
if (callers->call_stmt == stmt)
@@ -142,68 +286,192 @@ maybe_transactify_call (gimple stmt)
callers = callers->next_caller;
}
- /* Substitute call stmt. */
+ /* Substitute call stmt. */
if (callers)
{
gimple_call_set_fndecl (stmt, node->decl);
cgraph_redirect_edge_callee (callers, node);
if (dump_file)
- fprintf (dump_file, "redirected edge to %s\n",
- get_name (node->decl));
- redirected = true;
+ {
+ fprintf (dump_file, "redirected edge to");
+ print_generic_expr (dump_file, node->decl, 0);
+ fputc ('\n', dump_file);
+ }
+
+ state->subcode |= GTMA_HAVE_CALL_TM;
+ add_stmt_to_transaction (state, stmt);
+ return;
}
}
- /* In case the function call was not redirected and the function
- not marked as const or tm_pure, issue a warning. */
- /* ??? Handling of calls to irrevocable functions can be expanded here. */
- if (!redirected)
- warning (0, "Call to %qD potentially breaks isolation of transactions.",
- fn_decl);
+ /* The function was not const, tm_pure, or redirected to a
+ transactional clone. The call is therefore considered to
+ be irrevokable. */
+ state->subcode |= GTMA_HAVE_CALL_IRREVOKABLE;
+}
+
+/* Remove any calls to __tm_commit inside STATE which belong
+ to the transaction. */
+
+static void
+remove_tm_commits (struct ltm_state *state)
+{
+ gimple_stmt_iterator *gsi;
+ unsigned i;
+
+ for (i = 0;
+ VEC_iterate(gimple_stmt_iterator, state->commit_stmts, i, gsi);
+ ++i)
+ gsi_remove (gsi, true);
}
-/* This function expands the stmts within a transaction so that
- the corresponding STM versions of the stmt is called. */
+/* Lower a GIMPLE_TM_ATOMIC statement. The GSI is advanced. */
-static void ATTRIBUTE_UNUSED
-transactify_stmt (gimple_stmt_iterator *gsi)
+static void
+lower_tm_atomic (struct ltm_state *outer_state, gimple_stmt_iterator *gsi)
{
gimple stmt = gsi_stmt (*gsi);
+ struct ltm_state this_state;
+ struct eh_region *eh_region;
+ tree label;
+
+ this_state.subcode = 0;
+ this_state.region_nr = lookup_stmt_eh_region (stmt);
+ this_state.commit_stmts = VEC_alloc(gimple_stmt_iterator, heap, 1);
+
+ gcc_assert (this_state.region_nr >= 0);
+ eh_region = get_eh_region_from_number (this_state.region_nr);
+
+ /* First, lower the body. The scanning that we do inside gives
+ us some idea of what we're dealing with. */
+ lower_sequence_tm (&this_state, gimple_seq_body (stmt));
+
+ /* If there was absolutely nothing transaction related inside the
+ transaction, we may elide it. Likewise if this is a nested
+ transaction and does not contain an abort. */
+ if (this_state.subcode == 0
+ || (!(this_state.subcode & GTMA_HAVE_ABORT)
+ && outer_state != NULL))
+ {
+ if (outer_state)
+ outer_state->subcode |= this_state.subcode;
- switch (gimple_code (stmt))
+ remove_tm_commits (&this_state);
+ gsi_insert_seq_before (gsi, gimple_seq_body (stmt), GSI_SAME_STMT);
+ gimple_seq_set_body (stmt, NULL);
+ gsi_remove (gsi, true);
+ remove_eh_handler (eh_region);
+ goto fini;
+ }
+
+ /* Insert an EH_LABEL immediately after the GIMPLE_TM_ATOMIC node.
+ This label won't really be used, but it mimicks the effect of
+ the setjmp/longjmp that's going on behind the scenes. */
+ label = create_artificial_label ();
+ set_eh_region_tree_label (eh_region, label);
+ gsi_insert_after (gsi, gimple_build_label (label), GSI_CONTINUE_LINKING);
+
+ /* Insert the entire transaction sequence. */
+ gsi_insert_seq_after (gsi, gimple_seq_body (stmt), GSI_CONTINUE_LINKING);
+ gimple_seq_set_body (stmt, NULL);
+
+ /* Record a label at the end of the transaction that will be used in
+ case the transaction aborts. */
+ if (this_state.subcode & GTMA_HAVE_ABORT)
{
- case GIMPLE_CALL:
- maybe_transactify_call (stmt);
- break;
+ label = create_artificial_label ();
+ gimple_tm_atomic_set_label (stmt, label);
+ gsi_insert_after (gsi, gimple_build_label (label), GSI_CONTINUE_LINKING);
+ }
- case GIMPLE_ASSIGN:
- /* Only memory reads/writes need to be instrumented. */
- if (gimple_assign_single_p (stmt))
- maybe_transactify_assign (stmt);
- break;
+ /* Record the set of operations found for use during final lowering
+ of the GIMPLE_TM_ATOMIC node. */
+ gimple_tm_atomic_set_subcode (stmt, this_state.subcode);
- default:
- break;
+ /* Always update the iterator. */
+ gsi_next (gsi);
+
+ fini:
+ VEC_free (gimple_stmt_iterator, heap, this_state.commit_stmts);
+}
+
+/* Iterate through the statements in the sequence, lowering them all
+ as appropriate for being in a transaction. */
+
+static void
+lower_sequence_tm (struct ltm_state *state, gimple_seq seq)
+{
+ gimple_stmt_iterator gsi;
+
+ for (gsi = gsi_start (seq); !gsi_end_p (gsi); )
+ {
+ gimple stmt = gsi_stmt (gsi);
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_ASSIGN:
+ /* Only memory reads/writes need to be instrumented. */
+ if (gimple_assign_single_p (stmt))
+ lower_assign_tm (state, &gsi);
+ break;
+
+ case GIMPLE_CALL:
+ lower_call_tm (state, &gsi);
+ break;
+
+ case GIMPLE_TM_ATOMIC:
+ lower_tm_atomic (state, &gsi);
+ goto no_update;
+
+ default:
+ break;
+ }
+ gsi_next (&gsi);
+ no_update:
+ ;
}
}
-/* Main entry point for expanding TM-GIMPLE into runtime calls to the STM. */
+/* Iterate through the statements in the sequence, lowering them all
+ as appropriate for being outside of a transaction. */
-static unsigned int
-execute_expand_tm (void)
+static void
+lower_sequence_no_tm (gimple_seq seq)
{
- bool in_transaction = false;
+ gimple_stmt_iterator gsi;
+
+ for (gsi = gsi_start (seq); !gsi_end_p (gsi); )
+ if (gimple_code (gsi_stmt (gsi)) == GIMPLE_TM_ATOMIC)
+ lower_tm_atomic (NULL, &gsi);
+ else
+ gsi_next (&gsi);
+}
+
+/* Main entry point for flattening GIMPLE_TM_ATOMIC constructs. After
+ this, GIMPLE_TM_ATOMIC nodes still exist, but the nested body has
+ been moved out, and all the data required for constructing a proper
+ CFG has been recorded. */
+static unsigned int
+execute_lower_tm (void)
+{
/* Functions that are marked TM_PURE don't need annotation by definition. */
+ /* ??? The Intel OOPSLA paper talks about performing the same scan of the
+ function as we would if the function was marked DECL_IS_TM_CLONE, and
+ warning if we find anything for which we would have made a change. */
if (DECL_IS_TM_PURE (current_function_decl))
return 0;
/* When instrumenting a transactional clone, we begin the function inside
a transaction. */
if (DECL_IS_TM_CLONE (current_function_decl))
- in_transaction = true;
-
- /* Walk dominator tree expanding blocks. Seed with in_transaction. */
+ {
+ struct ltm_state state;
+ state.subcode = 0;
+ state.region_nr = -1;
+ lower_sequence_tm (&state, gimple_body (current_function_decl));
+ }
+ else
+ lower_sequence_no_tm (gimple_body (current_function_decl));
return 0;
}
@@ -216,227 +484,162 @@ gate_tm (void)
return flag_tm;
}
-struct gimple_opt_pass pass_expand_tm =
+struct gimple_opt_pass pass_lower_tm =
{
{
GIMPLE_PASS,
- "tmexp", /* name */
+ "tmlower", /* name */
gate_tm, /* gate */
- execute_expand_tm, /* execute */
+ execute_lower_tm, /* execute */
NULL, /* sub */
NULL, /* next */
0, /* static_pass_number */
0, /* tv_id */
- PROP_gimple_any, /* properties_required */
+ PROP_gimple_leh, /* properties_required */
0, /* properties_provided */
0, /* properties_destroyed */
0, /* todo_flags_start */
- TODO_dump_func
- | TODO_cleanup_cfg
- | TODO_ggc_collect, /* todo_flags_finish */
+ TODO_dump_func /* todo_flags_finish */
}
};
+
-
-#if 0
-/* Calculate live ranges on SSA. Then checkpoint the
- live-in variables to the transaction. */
+/* ??? Find real values for these bits. */
+#define TM_START_RESTORE_LIVE_IN 1
+#define TM_START_ABORT 2
+
+/* All local variables that have been modified since the beginning of the
+ transaction up until the last possible transaction restart need to be
+ reset when we restart the transaction.
+
+ Here we implement this by replacing the new SSA_NAME created by the
+ PHI at the join of the abnormal backedges by the old SSA_NAME that
+ was originally live at entry to the transaction. This does result in
+ the old SSA_NAME continuing to stay live when new values are defined,
+ and this isn't necessarily the most efficient implementation, but it
+ is just about the easiest. */
static void
-checkpoint_live_in_variables (struct tm_region *region,
- gimple_stmt_iterator *gsi_recover,
- basic_block begin_bb)
-{
- int index = begin_bb->index;
- block_stmt_iterator bsi_save = bsi_for_stmt (region->setjmp_stmt);
- basic_block save_bb = bb_for_stmt (region->setjmp_stmt);
- basic_block recover_bb = bb_for_stmt (bsi_stmt (*bsi_recover));
- tree ssa_var;
- tree_live_info_p liveinfo;
- var_map map;
- int p;
- tree rep;
- unsigned int i;
- unsigned int j;
- bitmap_iterator bi;
-
- map = init_var_map (num_ssa_names + 1);
-
- /* Create liveness information for each SSA_NAME. */
- for (j = 0; j < num_ssa_names; j++)
- {
- ssa_var = ssa_name (j);
- if (!ssa_var)
- continue;
-
- if (TREE_CODE (ssa_var) == SSA_NAME)
- {
- register_ssa_partition (map, ssa_var);
- p = partition_find (map->var_partition, SSA_NAME_VERSION (ssa_var));
- gcc_assert (p != NO_PARTITION);
- rep = partition_to_var (map, p);
- }
- }
-
- liveinfo = calculate_live_ranges (map);
+checkpoint_live_in_variables (edge e)
+{
+ gimple_stmt_iterator gsi;
- /* If variable is live-in at beginning of the
- transaction checkpoint its value. */
- if (liveinfo->livein)
+ for (gsi = gsi_start_phis (e->dest); !gsi_end_p (gsi); )
{
- if (dump_file)
- fprintf (dump_file, "\nCheckpoint variables for transaction. BB %d : ",
index);
-
- EXECUTE_IF_SET_IN_BITMAP (liveinfo->livein[index], 0, i, bi)
+ gimple phi = gsi_stmt (gsi);
+ tree old_ssa, new_ssa;
+ unsigned i, n;
+
+ new_ssa = gimple_phi_result (phi);
+ old_ssa = gimple_phi_arg_def (phi, e->dest_idx);
+
+ /* We need to recompute SSA_NAME_OCCURS_IN_ABNORMAL_PHI on each
+ of the other arguments to the PHI, discounting the one abnormal
+ phi node that we're about to delete. */
+ for (i = 0, n = gimple_phi_num_args (phi); i < n; ++i)
{
- tree var = partition_to_var (map, i);
+ tree arg = gimple_phi_arg_def (phi, i);
+ imm_use_iterator imm_iter;
+ use_operand_p use_p;
+ bool in_abnormal_phi;
+
+ if (TREE_CODE (arg) != SSA_NAME
+ || !SSA_NAME_OCCURS_IN_ABNORMAL_PHI (arg))
+ continue;
- /* TODO check restricts the use of temporaries by the compiler
- may impact other optimisations.
- Maybe reordering this part of the checkpointing before introducing
- temporary variables would avoid this check. */
- if ((!DECL_ARTIFICIAL (SSA_NAME_VAR (var)))
- && (!POINTER_TYPE_P (TREE_TYPE (var))))
+ in_abnormal_phi = false;
+ FOR_EACH_IMM_USE_FAST (use_p, imm_iter, arg)
{
- if (dump_file)
+ gimple phi2 = USE_STMT (use_p);
+ if (gimple_code (phi2) == GIMPLE_PHI && phi2 != phi)
{
- print_generic_expr (dump_file, var, TDF_SLIM);
- fprintf (dump_file, " ");
- }
- /* Create name for temporary variable
- that checkpoints value of var. */
- const char* orig = get_name (SSA_NAME_VAR (var));
- int len = strlen (orig);
- char *name = xmalloc (sizeof (char) * (len + 10));
- strncpy (name, "txn_save_", 9);
- strncpy (name + 9, orig, len);
- *(name + len + 9) = '\0';
-
- /* Create temporary. */
- tree type = TREE_TYPE (var);
- tree save = create_tmp_var (type, name);
- add_referenced_var (save);
- tree stmt;
-
- /* Create gimple statement for saving value of var. */
- stmt = fold_build2 (GIMPLE_MODIFY_STMT, type, save, var);
- tree real_save = make_ssa_name (save, stmt);
- SSA_NAME_OCCURS_IN_ABNORMAL_PHI (real_save) = true;
- GIMPLE_STMT_OPERAND (stmt, 0) = real_save;
-
- bsi_insert_before (&bsi_save, stmt, BSI_SAME_STMT);
-
- /* Create gimple statement for restoring value of var. */
- stmt = fold_build2 (GIMPLE_MODIFY_STMT, type, var, real_save);
- tree new_var = make_ssa_name (SSA_NAME_VAR (var), stmt);
- GIMPLE_STMT_OPERAND (stmt, 0) = new_var;
- bsi_insert_before (bsi_recover, stmt, BSI_SAME_STMT);
-
- /* Merge saved or recovered values before next basic block. */
- tree phi = create_phi_node (SSA_NAME_VAR (var), begin_bb);
- add_phi_arg (phi, new_var, FALLTHRU_EDGE (recover_bb));
- add_phi_arg (phi, var, FALLTHRU_EDGE (save_bb));
- tree new_var_phi = PHI_RESULT (phi);
-
- free_dominance_info (CDI_DOMINATORS);
- calculate_dominance_info (CDI_DOMINATORS);
-
- tree stmt2;
- imm_use_iterator iter;
- use_operand_p use_p;
- FOR_EACH_IMM_USE_STMT (stmt2, iter, var)
- {
- if (stmt2 == phi)
- continue;
-
- basic_block tmp_bb = bb_for_stmt (stmt2);
- if (dominated_by_p (CDI_DOMINATORS, tmp_bb, begin_bb))
+ unsigned ix = PHI_ARG_INDEX_FROM_USE (use_p);
+ if (gimple_phi_arg_edge (phi2, ix)->flags & EDGE_ABNORMAL)
{
- FOR_EACH_IMM_USE_ON_STMT (use_p, iter)
- propagate_value (use_p, new_var_phi);
+ in_abnormal_phi = true;
+ break;
}
}
}
+ SSA_NAME_OCCURS_IN_ABNORMAL_PHI (arg) = in_abnormal_phi;
}
- if (dump_file)
- fprintf (dump_file, "\n");
+ replace_uses_by (new_ssa, old_ssa);
+ remove_phi_node (&gsi, true);
}
- update_ssa(TODO_update_ssa);
-
- return ;
}
-/* Implements the checkpointing of transactions. */
static void
-checkpoint_tm_txn (struct tm_region *region)
+expand_tm_atomic (basic_block bb, gimple_stmt_iterator *gsi)
{
- basic_block entry_bb = bb_for_stmt (region->setjmp_stmt);
-
- edge branch = BRANCH_EDGE (entry_bb);
- edge fall = FALLTHRU_EDGE (entry_bb);
+ tree status, tm_start;
+ basic_block body_bb, test_bb;
+ gimple_stmt_iterator gsi2;
+ tree t1, t2;
+ gimple g;
+ edge e;
+
+ tm_start = built_in_decls[BUILT_IN_TM_START];
+ status = make_rename_temp (TREE_TYPE (TREE_TYPE (tm_start)), "tm_state");
+
+ e = FALLTHRU_EDGE (bb);
+ body_bb = e->dest;
+ checkpoint_live_in_variables (e);
- basic_block begin_bb = fall->dest;
- basic_block recover_bb = branch->dest;
- basic_block next_bb = single_succ (recover_bb);
-
- gcc_assert(begin_bb == next_bb);
- block_stmt_iterator bsi_recover = bsi_start (recover_bb);
- gcc_assert (TREE_CODE (bsi_stmt (bsi_recover)) == LABEL_EXPR);
-
- bsi_next (&bsi_recover);
-
- checkpoint_live_in_variables (region, &bsi_recover, begin_bb);
-}
-
-/* Walk the region tree and start checkpointing. */
-static void
-checkpoint_tm (struct tm_region *region)
-{
- while (region)
+ if (gimple_tm_atomic_label (gsi_stmt (*gsi)))
{
- /* First, introduce checkpoints for the inner regions.
- TODO: testing. Overlapping of inner and outer
- regions not handled correctly.
- Nesting of transactions not implemented correctly.*/
- if (region->inner)
- {
- checkpoint_tm_txn (region->inner);
- }
-
- checkpoint_tm_txn (region);
-
- region = region->next;
+ e = split_block_after_labels (body_bb);
+ test_bb = e->src;
+ body_bb = e->dest;
+
+ gsi2 = gsi_last_bb (test_bb);
+
+ t1 = make_rename_temp (TREE_TYPE (status), NULL);
+ t2 = build_int_cst (TREE_TYPE (status), TM_START_ABORT);
+ g = gimple_build_assign_with_ops (BIT_AND_EXPR, t1, status, t2);
+ gsi_insert_after (&gsi2, g, GSI_CONTINUE_LINKING);
+
+ t2 = build_int_cst (TREE_TYPE (status), 0);
+ g = gimple_build_cond (NE_EXPR, t1, t2, NULL, NULL);
+ gsi_insert_after (&gsi2, g, GSI_CONTINUE_LINKING);
+
+ single_succ_edge (test_bb)->flags = EDGE_FALSE_VALUE;
+
+ e = BRANCH_EDGE (bb);
+ redirect_edge_pred (e, test_bb);
+ e->flags = EDGE_TRUE_VALUE;
}
+
+ /* ??? Need to put the real input to __tm_start here. */
+ t2 = build_int_cst (TREE_TYPE (status), 0);
+ g = gimple_build_call (tm_start, 1, t2);
+ gimple_call_set_lhs (g, status);
+ gsi_insert_before (gsi, g, GSI_SAME_STMT);
+ gsi_remove (gsi, true);
}
/* Entry point to the checkpointing. */
-void
+
+static unsigned int
execute_checkpoint_tm (void)
{
- /* Regions are built during TM expansion pass. */
- if (!root_tm_region)
- return;
-
- /* Checkpointing is done here. */
- checkpoint_tm (root_tm_region);
+ basic_block bb;
- if (dump_file)
+ FOR_EACH_BB (bb)
{
- fprintf (dump_file, "\nTM region tree after checkpointing\n\n");
- dump_tm_region (dump_file, root_tm_region, 0);
- fprintf (dump_file, "\n");
+ gimple_stmt_iterator gsi = gsi_last_bb (bb);
+ if (!gsi_end_p (gsi)
+ && gimple_code (gsi_stmt (gsi)) == GIMPLE_TM_ATOMIC)
+ expand_tm_atomic (bb, &gsi);
}
- free_dominance_info (CDI_DOMINATORS);
- cleanup_tree_cfg ();
- free_tm_regions ();
-
- return;
+ return 0;
}
-struct tree_opt_pass pass_checkpoint_tm =
+struct gimple_opt_pass pass_checkpoint_tm =
{
+ {
+ GIMPLE_PASS,
"tmcheckpoint", /* name */
gate_tm, /* gate */
execute_checkpoint_tm, /* execute */
@@ -451,6 +654,84 @@ struct tree_opt_pass pass_checkpoint_tm
TODO_update_ssa |
TODO_verify_ssa |
TODO_dump_func, /* todo_flags_finish */
- 0 /* letter */
+ }
};
-#endif
+
+/* Construct transaction restart edges for STMT. */
+
+static void
+make_tm_edge_1 (struct eh_region *region, void *data)
+{
+ gimple stmt = (gimple) data;
+ basic_block src, dst;
+ unsigned flags;
+
+ src = gimple_bb (stmt);
+ dst = label_to_block (get_eh_region_tree_label (region));
+
+ /* Don't set EDGE_EH here, because that's supposed to be used when
+ we could in principal redirect the edge by modifying the exception
+ tables. Transactions don't use tables though, only setjmp. */
+ flags = EDGE_ABNORMAL;
+ if (gimple_code (stmt) == GIMPLE_CALL)
+ flags |= EDGE_ABNORMAL_CALL;
+ make_edge (src, dst, flags);
+}
+
+void
+make_tm_edge (gimple stmt)
+{
+ int region_nr;
+
+ /* Do nothing if the region is outside this function. */
+ region_nr = lookup_stmt_eh_region (stmt);
+ if (region_nr < 0)
+ return;
+
+ /* The control structure inside tree-cfg.c isn't the best;
+ re-check whether this is actually a transactional stmt. */
+ if (!is_transactional_stmt (stmt))
+ return;
+
+ foreach_reachable_transaction (region_nr, make_tm_edge_1, (void *) stmt);
+}
+
+/* Return true if STMT may alter control flow via a transactional edge. */
+
+bool
+is_transactional_stmt (const_gimple stmt)
+{
+ switch (gimple_code (stmt))
+ {
+ case GIMPLE_ASSIGN:
+ {
+ /* We only want to process assignments that have been
+ marked for transactional memory. */
+ enum tree_code subcode = gimple_expr_code (stmt);
+ return (subcode == TM_LOAD || subcode == TM_STORE);
+ }
+
+ case GIMPLE_CALL:
+ {
+ tree fn_decl = gimple_call_fndecl (stmt);
+
+ /* We only want to process __tm_abort and cloned direct calls,
+ since those are the only ones that can restart the transaction. */
+ if (!fn_decl)
+ return false;
+ if (DECL_BUILT_IN_CLASS (fn_decl) == BUILT_IN_NORMAL
+ && DECL_FUNCTION_CODE (fn_decl) == BUILT_IN_TM_ABORT)
+ return true;
+ if (DECL_IS_TM_CLONE (fn_decl))
+ return true;
+ else
+ return false;
+ }
+
+ case GIMPLE_TM_ATOMIC:
+ return true;
+
+ default:
+ return false;
+ }
+}
Index: gimple-low.c
===================================================================
--- gimple-low.c (revision 141199)
+++ gimple-low.c (revision 141200)
@@ -77,16 +77,12 @@ struct lower_data
/* True if the function calls __builtin_setjmp. */
bool calls_builtin_setjmp;
-
- /* True if we're lowering inside a transaction. */
- bool in_transaction;
};
static void lower_stmt (gimple_stmt_iterator *, struct lower_data *);
static void lower_gimple_bind (gimple_stmt_iterator *, struct lower_data *);
static void lower_gimple_return (gimple_stmt_iterator *, struct lower_data *);
static void lower_builtin_setjmp (gimple_stmt_iterator *);
-static void record_vars_into_tm (tree, tree, bool);
/* Lower the body of current_function_decl from High GIMPLE into Low
@@ -236,31 +232,6 @@ lower_sequence (gimple_seq seq, struct l
}
-/* Lower the __tm_atomic statement pointed by TSI. DATA is
- passed through the recursion. */
-
-static void
-lower_tm_atomic (gimple_stmt_iterator *gsi, struct lower_data *data)
-{
- bool old_in_transaction = data->in_transaction;
- gimple stmt = gsi_stmt (*gsi);
- tree label = create_artificial_label ();
-
- data->in_transaction = true;
-
- lower_sequence (gimple_seq_body (stmt), data);
-
- gsi_insert_before (gsi, stmt, GSI_SAME_STMT);
- gsi_insert_seq_before (gsi, gimple_seq_body (stmt), GSI_SAME_STMT);
- gsi_insert_before (gsi, gimple_build_label (label), GSI_SAME_STMT);
-
- gimple_seq_set_body (stmt, NULL);
- gimple_tm_atomic_set_label (stmt, label);
- gsi_remove (gsi, false);
-
- data->in_transaction = old_in_transaction;
-}
-
/* Lower the OpenMP directive statement pointed by GSI. DATA is
passed through the recursion. */
@@ -358,8 +329,8 @@ lower_stmt (gimple_stmt_iterator *gsi, s
return;
case GIMPLE_TM_ATOMIC:
- lower_tm_atomic (gsi, data);
- return;
+ lower_sequence (gimple_seq_body (stmt), data);
+ break;
default:
gcc_unreachable ();
@@ -405,8 +376,7 @@ lower_gimple_bind (gimple_stmt_iterator
}
}
- record_vars_into_tm (gimple_bind_vars (stmt), current_function_decl,
- data->in_transaction);
+ record_vars (gimple_bind_vars (stmt));
lower_sequence (gimple_bind_body (stmt), data);
if (new_block)
@@ -820,11 +790,10 @@ lower_builtin_setjmp (gimple_stmt_iterat
}
-/* Record the variables in VARS into function FN. If IN_TRANSACTION is
- true, mark them DECL_IS_TM_PURE_VAR. */
+/* Record the variables in VARS into function FN. */
-static void
-record_vars_into_tm (tree vars, tree fn, bool in_transaction)
+void
+record_vars_into (tree vars, tree fn)
{
if (fn != current_function_decl)
push_cfun (DECL_STRUCT_FUNCTION (fn));
@@ -843,36 +812,21 @@ record_vars_into_tm (tree vars, tree fn,
continue;
/* Record the variable. */
- cfun->local_decls = tree_cons (NULL_TREE, var,
- cfun->local_decls);
-
- /* If we're inside a transaction, mark it for NOT checkpointing. */
- if (in_transaction)
- DECL_IS_TM_PURE_VAR (var) = 1;
+ cfun->local_decls = tree_cons (NULL_TREE, var, cfun->local_decls);
}
if (fn != current_function_decl)
pop_cfun ();
}
-/* Record the variables in VARS into function FN. */
-
-void
-record_vars_into (tree vars, tree fn)
-{
- record_vars_into_tm (vars, fn, false);
-}
-
-
/* Record the variables in VARS into current_function_decl. */
void
record_vars (tree vars)
{
- record_vars_into_tm (vars, current_function_decl, false);
+ record_vars_into (vars, current_function_decl);
}
-
/* Mark BLOCK used if it has a used variable in it, then recurse over its
subblocks. */
Index: tree-eh.c
===================================================================
--- tree-eh.c (revision 141199)
+++ tree-eh.c (revision 141200)
@@ -310,6 +310,10 @@ collect_finally_tree (gimple stmt, gimpl
collect_finally_tree_1 (gimple_eh_filter_failure (stmt), region);
break;
+ case GIMPLE_TM_ATOMIC:
+ collect_finally_tree_1 (gimple_seq_body (stmt), region);
+ break;
+
default:
/* A type, a decl, or some kind of statement that we're not
interested in. Don't walk them. */
@@ -1869,6 +1873,21 @@ lower_eh_constructs_2 (struct leh_state
/* Return since we don't want gsi_next () */
return;
+ case GIMPLE_TM_ATOMIC:
+ {
+ /* Record the transaction region in the EH tree, then process
+ the body of the transaction. We don't lower the transaction
+ node just yet. */
+ struct eh_region *outer = state->cur_region;
+ state->cur_region = gen_eh_region_transaction (outer);
+
+ record_stmt_eh_region (state->cur_region, stmt);
+ lower_eh_constructs_1 (state, gimple_seq_body (stmt));
+
+ state->cur_region = outer;
+ }
+ break;
+
default:
/* A type, a decl, or some kind of statement that we're not
interested in. Don't walk them. */
@@ -2043,6 +2062,9 @@ verify_eh_edges (gimple stmt)
}
if (!stmt_could_throw_p (stmt))
{
+ /* ??? Add bits to verify transactional edges. */
+ if (is_transactional_stmt (stmt))
+ return false;
error ("BB %i last statement has incorrectly set region", bb->index);
return true;
}
Index: gimple-pretty-print.c
===================================================================
--- gimple-pretty-print.c (revision 141199)
+++ gimple-pretty-print.c (revision 141200)
@@ -161,6 +161,7 @@ debug_gimple_seq (gimple_seq seq)
'd' - outputs an int as a decimal,
's' - outputs a string,
'n' - outputs a newline,
+ 'x' - outputs an int as hexadecimal,
'+' - increases indent by 2 then outputs a newline,
'-' - decreases indent by 2 then outputs a newline. */
@@ -215,6 +216,10 @@ dump_gimple_fmt (pretty_printer *buffer,
newline_and_indent (buffer, spc);
break;
+ case 'x':
+ pp_scalar (buffer, "%x", va_arg (args, int));
+ break;
+
case '+':
spc += 2;
newline_and_indent (buffer, spc);
@@ -350,9 +355,19 @@ dump_binary_rhs (pretty_printer *buffer,
static void
dump_gimple_assign (pretty_printer *buffer, gimple gs, int spc, int flags)
{
+ enum tree_code code;
+
+ /* Don't bypass the transactional markers like
+ gimple_assign_rhs_code would. */
+ code = gimple_expr_code (gs);
+ if (code != TM_LOAD && code != TM_STORE
+ && get_gimple_rhs_class (code) == GIMPLE_SINGLE_RHS)
+ code = TREE_CODE (gimple_assign_rhs1 (gs));
+
if (flags & TDF_RAW)
{
tree last;
+
if (gimple_num_ops (gs) == 2)
last = NULL_TREE;
else if (gimple_num_ops (gs) == 3)
@@ -361,8 +376,8 @@ dump_gimple_assign (pretty_printer *buff
gcc_unreachable ();
dump_gimple_fmt (buffer, spc, flags, "%G <%s, %T, %T, %T>", gs,
- tree_code_name[gimple_assign_rhs_code (gs)],
- gimple_assign_lhs (gs), gimple_assign_rhs1 (gs), last);
+ tree_code_name[code], gimple_assign_lhs (gs),
+ gimple_assign_rhs1 (gs), last);
}
else
{
@@ -372,6 +387,11 @@ dump_gimple_assign (pretty_printer *buff
pp_space (buffer);
pp_character (buffer, '=');
+ if (code == TM_LOAD)
+ pp_string (buffer, "{tm_load}");
+ else if (code == TM_STORE)
+ pp_string (buffer, "{tm_store}");
+
if (gimple_assign_nontemporal_move_p (gs))
pp_string (buffer, "{nt}");
@@ -1013,11 +1033,18 @@ static void
dump_gimple_tm_atomic (pretty_printer *buffer, gimple gs, int spc, int flags)
{
if (flags & TDF_RAW)
- dump_gimple_fmt (buffer, spc, flags, "%G <%+BODY <%S> >", gs,
- gimple_seq_body (gs));
+ dump_gimple_fmt (buffer, spc, flags,
+ "%G [SUBCODE=%x,LABEL=%T] <%+BODY <%S> >",
+ gs, gimple_tm_atomic_subcode (gs),
+ gimple_tm_atomic_label (gs), gimple_seq_body (gs));
else
{
- pp_string (buffer, "__tm_atomic");
+ pp_printf (buffer, "__tm_atomic [SUBCODE=%x,LABEL=",
+ gimple_tm_atomic_subcode (gs));
+ dump_generic_node (buffer, gimple_tm_atomic_label (gs),
+ spc, flags, false);
+ pp_string (buffer, "]");
+
if (!gimple_seq_empty_p (gimple_seq_body (gs)))
{
newline_and_indent (buffer, spc + 2);
Index: except.c
===================================================================
--- except.c (revision 141199)
+++ except.c (revision 141200)
@@ -140,7 +140,8 @@ struct eh_region GTY(())
ERT_CATCH,
ERT_ALLOWED_EXCEPTIONS,
ERT_MUST_NOT_THROW,
- ERT_THROW
+ ERT_THROW,
+ ERT_TRANSACTION
} type;
/* Holds the action to perform based on the preceding type. */
@@ -178,6 +179,11 @@ struct eh_region GTY(())
struct eh_region_u_cleanup {
struct eh_region *prev_try;
} GTY ((tag ("ERT_CLEANUP"))) cleanup;
+
+ /* ??? Nothing for now. */
+ struct eh_region_u_transaction {
+ int dummy;
+ } GTY ((tag ("ERT_TRANSACTION"))) transaction;
} GTY ((desc ("%0.type"))) u;
/* Entry point for this region's handler before landing pads are built. */
@@ -253,7 +259,6 @@ static hashval_t ehl_hash (const void *)
static int ehl_eq (const void *, const void *);
static void add_ehl_entry (rtx, struct eh_region *);
static void remove_exception_handler_label (rtx);
-static void remove_eh_handler (struct eh_region *);
static int for_each_eh_label_1 (void **, void *);
/* The return value of reachable_next_level. */
@@ -422,7 +427,7 @@ gen_eh_region (enum eh_region_type type,
struct eh_region *new_eh;
#ifdef ENABLE_CHECKING
- gcc_assert (doing_eh (0));
+ gcc_assert (flag_tm || doing_eh (0));
#endif
/* Insert a new blank region as a leaf in the tree. */
@@ -509,12 +514,24 @@ gen_eh_region_must_not_throw (struct eh_
return gen_eh_region (ERT_MUST_NOT_THROW, outer);
}
+struct eh_region *
+gen_eh_region_transaction (struct eh_region *outer)
+{
+ return gen_eh_region (ERT_TRANSACTION, outer);
+}
+
int
get_eh_region_number (struct eh_region *region)
{
return region->region_number;
}
+struct eh_region *
+get_eh_region_from_number (int region_nr)
+{
+ return VEC_index (eh_region, cfun->eh->region_array, region_nr);
+}
+
bool
get_eh_region_may_contain_throw (struct eh_region *region)
{
@@ -808,7 +825,8 @@ current_function_has_exception_handlers
region = VEC_index (eh_region, cfun->eh->region_array, i);
if (region
&& region->region_number == i
- && region->type != ERT_THROW)
+ && region->type != ERT_THROW
+ && region->type != ERT_TRANSACTION)
return true;
}
@@ -1473,6 +1491,7 @@ build_post_landing_pads (void)
case ERT_CATCH:
case ERT_THROW:
+ case ERT_TRANSACTION:
/* Nothing to do. */
break;
@@ -2142,7 +2161,7 @@ remove_exception_handler_label (rtx labe
/* Splice REGION from the region tree etc. */
-static void
+void
remove_eh_handler (struct eh_region *region)
{
struct eh_region **pp, **pp_start, *p, *outer, *inner;
@@ -2524,6 +2543,11 @@ reachable_next_level (struct eh_region *
else
return RNL_BLOCKED;
+ case ERT_TRANSACTION:
+ /* Transaction regions don't catch exceptions, they catch
+ transaction restarts. */
+ return RNL_NOT_CAUGHT;
+
case ERT_THROW:
case ERT_UNKNOWN:
/* Shouldn't see these here. */
@@ -2538,8 +2562,7 @@ reachable_next_level (struct eh_region *
void
foreach_reachable_handler (int region_number, bool is_resx,
- void (*callback) (struct eh_region *, void *),
- void *callback_data)
+ eh_callback callback, void *callback_data)
{
struct reachable_info info;
struct eh_region *region;
@@ -2581,6 +2604,26 @@ foreach_reachable_handler (int region_nu
}
}
+/* Invoke CALLBACK for each TRANSACTION region inside REGION_NUMBER. */
+
+void
+foreach_reachable_transaction (int region_number,
+ eh_callback callback, void *callback_data)
+{
+ struct eh_region *region;
+
+ region = VEC_index (eh_region, cfun->eh->region_array, region_number);
+ while (region)
+ {
+ if (region->type == ERT_TRANSACTION)
+ {
+ callback (region, callback_data);
+ break;
+ }
+ region = region->outer;
+ }
+}
+
/* Retrieve a list of labels of exception handlers which can be
reached by a given insn. */
@@ -3177,8 +3220,10 @@ collect_one_action_chain (htab_t ar_hash
case ERT_CATCH:
case ERT_THROW:
+ case ERT_TRANSACTION:
/* CATCH regions are handled in TRY above. THROW regions are
- for optimization information only and produce no output. */
+ for optimization information only and produce no output.
+ TRANSACTION regions produce no output as well. */
return collect_one_action_chain (ar_hash, region->outer);
default:
Index: except.h
===================================================================
--- except.h (revision 141199)
+++ except.h (revision 141200)
@@ -92,14 +92,16 @@ extern struct eh_region *gen_eh_region_t
extern struct eh_region *gen_eh_region_catch (struct eh_region *, tree);
extern struct eh_region *gen_eh_region_allowed (struct eh_region *, tree);
extern struct eh_region *gen_eh_region_must_not_throw (struct eh_region *);
+extern struct eh_region *gen_eh_region_transaction (struct eh_region *);
extern int get_eh_region_number (struct eh_region *);
+extern struct eh_region *get_eh_region_from_number (int);
extern bool get_eh_region_may_contain_throw (struct eh_region *);
extern tree get_eh_region_tree_label (struct eh_region *);
extern void set_eh_region_tree_label (struct eh_region *, tree);
-extern void foreach_reachable_handler (int, bool,
- void (*) (struct eh_region *, void *),
- void *);
+typedef void (*eh_callback) (struct eh_region *, void *);
+extern void foreach_reachable_handler (int, bool, eh_callback, void *);
+extern void foreach_reachable_transaction (int, eh_callback, void *);
extern void collect_eh_region_array (void);
extern void expand_resx_expr (tree);
@@ -107,6 +109,7 @@ extern void verify_eh_tree (struct funct
extern void dump_eh_tree (FILE *, struct function *);
extern bool eh_region_outer_p (struct function *, int, int);
extern int eh_region_outermost (struct function *, int, int);
+extern void remove_eh_handler (struct eh_region *);
/* If non-NULL, this is a function that returns an expression to be
executed if an unhandled exception is propagated out of a cleanup
Index: ChangeLog.tm
===================================================================
--- ChangeLog.tm (revision 141199)
+++ ChangeLog.tm (revision 141200)
@@ -1,3 +1,51 @@
+2008-10-17 Richard Henderson <r...@redhat.com>
+
+ * except.c (struct eh_region): Add ERT_TRANSACTION.
+ (gen_eh_region): Allow if flag_tm.
+ (gen_eh_region_transaction, get_eh_region_from_number): New.
+ (remove_eh_handler): Export.
+ (current_function_has_exception_handlers): Handle ERT_TRANSACTION.
+ (build_post_landing_pads, reachable_next_level): Likewise.
+ (collect_one_action_chain): Likewise.
+ (foreach_reachable_transaction): New.
+ * except.h: Add new exported decls.
+ * gimple-low.c (struct lower_data): Remove in_transaction.
+ (lower_tm_atomic, record_vars_into_tm): Remove.
+ * gimple-pretty-print.c (dump_gimple_fmt): Add %x.
+ (dump_gimple_assign): Handle TM_LOAD/STORE.
+ (dump_gimple_tm_atomic): Dump the subcode.
+ * gimple.h (GTMA_HAVE_ABORT, GTMA_HAVE_LOAD, GTMA_HAVE_STORE,
+ GTMA_HAVE_CALL_TM, GTMA_HAVE_CALL_IRREVOKABLE,
+ GTMA_MUST_CALL_IRREVOKABLE, GTMA_HAVE_CALL_INDIRECT): New.
+ (gimple_tm_atomic_subcode, gimple_tm_atomic_set_subcode): New.
+ * gtm-low.c (struct ltm_state, add_stmt_to_transaction,
+ lower_assign_tm, lower_call_tm, remove_tm_commits,
+ lower_tm_atomic, lower_sequence_tm, lower_sequence_no_tm): New.
+ (execute_lower_tm): Use them.
+ (TM_START_RESTORE_LIVE_IN, TM_START_ABORT): New.
+ (checkpoint_live_in_variables): Rewrite.
+ (checkpoint_tm_txn, checkpoint_tm): Remove.
+ (expand_tm_atomic): New.
+ (execute_checkpoint_tm): Use it.
+ (make_tm_edge_1, make_tm_edge, is_transactional_stmt): New.
+ (pass_lower_tm): Rename from pass_expand_tm.
+ * passes.c (init_optimization_passes): Run pass_lower_tm
+ immediately after pass_lower_eh. Run pass_checkpoint_tm
+ after early optimizations.
+ * tree-cfg.c (make_edges): Call make_tm_edge. Conditionally
+ create the __tm_atomic abort edge.
+ (cleanup_dead_labels): Handle GIMPLE_TM_ATOMIC. Avoid unnecessary
+ writes into the statements to update labels.
+ (is_ctrl_altering_stmt): Include is_transactional_stmt.
+ (verify_stmt): Handle transactional edges.
+ * tree-eh.c (collect_finally_tree): Walk GIMPLE_TM_ATOMIC.
+ (lower_eh_constructs_2): Create EH regions for them.
+ (verify_eh_edges): Handle transactional edges.
+ * tree-flow.h (make_tm_edge, is_transactional_stmt): Declare.
+
+ * c-parser.c (c_parser_tm_abort): Call add_stmt.
+ * cgraphbuild.c (prepare_tm_clone): Disable for now.
+
2008-10-15 Richard Henderson <r...@redhat.com>
* builtin-attrs.def (ATTR_RETURNS_TWICE): New.
Index: tree-flow.h
===================================================================
--- tree-flow.h (revision 141199)
+++ tree-flow.h (revision 141200)
@@ -1079,6 +1079,9 @@ extern int lookup_expr_eh_region (tree);
extern int lookup_stmt_eh_region (gimple);
extern bool verify_eh_edges (gimple);
+/* In gtm-low.c */
+extern void make_tm_edge (gimple);
+extern bool is_transactional_stmt (const_gimple);
/* In tree-ssa-pre.c */
struct pre_expr_d;
Index: gimple.h
===================================================================
--- gimple.h (revision 141199)
+++ gimple.h (revision 141200)
@@ -735,6 +735,15 @@ struct gimple_statement_omp_atomic_store
/* GIMPLE_TM_ATOMIC. */
+/* Bits to be stored in the GIMPLE_TM_ATOMIC subcode. */
+#define GTMA_HAVE_ABORT (1u << 0)
+#define GTMA_HAVE_LOAD (1u << 1)
+#define GTMA_HAVE_STORE (1u << 2)
+#define GTMA_HAVE_CALL_TM (1u << 3)
+#define GTMA_HAVE_CALL_IRREVOKABLE (1u << 4)
+#define GTMA_MUST_CALL_IRREVOKABLE (1u << 5)
+#define GTMA_HAVE_CALL_INDIRECT (1u << 6)
+
struct gimple_statement_tm_atomic GTY(())
{
/* [ WORD 1-5 ] */
@@ -4134,6 +4143,15 @@ gimple_tm_atomic_label (const_gimple gs)
return gs->gimple_tm_atomic.label;
}
+/* Return the subcode associated with a GIMPLE_TM_ATOMIC. */
+
+static inline unsigned int
+gimple_tm_atomic_subcode (const_gimple gs)
+{
+ GIMPLE_CHECK (gs, GIMPLE_TM_ATOMIC);
+ return gs->gsbase.subcode;
+}
+
/* Set the label associated with a GIMPLE_TM_ATOMIC. */
static inline void
@@ -4143,6 +4161,16 @@ gimple_tm_atomic_set_label (gimple gs, t
gs->gimple_tm_atomic.label = label;
}
+/* Set the subcode associated with a GIMPLE_TM_ATOMIC. */
+
+static inline void
+gimple_tm_atomic_set_subcode (gimple gs, unsigned int subcode)
+{
+ GIMPLE_CHECK (gs, GIMPLE_TM_ATOMIC);
+ gs->gsbase.subcode = subcode;
+}
+
+
/* Return a pointer to the return value for GIMPLE_RETURN GS. */
static inline tree *
Index: tree-cfg.c
===================================================================
--- tree-cfg.c (revision 141199)
+++ tree-cfg.c (revision 141200)
@@ -514,6 +514,10 @@ make_edges (void)
create abnormal edges to them. */
make_eh_edges (last);
+ /* If this statement calls a transaction clone,
+ add transactional restart edges. */
+ make_tm_edge (last);
+
/* Some calls are known not to return. */
fallthru = !(gimple_call_flags (last) & ECF_NORETURN);
break;
@@ -524,6 +528,7 @@ make_edges (void)
if (is_ctrl_altering_stmt (last))
{
make_eh_edges (last);
+ make_tm_edge (last);
}
fallthru = true;
break;
@@ -613,11 +618,12 @@ make_edges (void)
break;
case GIMPLE_TM_ATOMIC:
- /* ??? The edge from __tm_atomic to the out-label is only
- used when __tm_abort is present. Detect that's not
- present and omit it. */
- make_edge (bb, label_to_block (gimple_tm_atomic_label (last)), 0);
- fallthru = true;
+ {
+ tree abort_label = gimple_tm_atomic_label (last);
+ if (abort_label)
+ make_edge (bb, label_to_block (abort_label), 0);
+ fallthru = true;
+ }
break;
default:
@@ -988,22 +994,30 @@ cleanup_dead_labels (void)
FOR_EACH_BB (bb)
{
gimple stmt = last_stmt (bb);
+ tree label, new_label;
+
if (!stmt)
continue;
switch (gimple_code (stmt))
{
case GIMPLE_COND:
- {
- tree true_label = gimple_cond_true_label (stmt);
- tree false_label = gimple_cond_false_label (stmt);
+ label = gimple_cond_true_label (stmt);
+ if (label)
+ {
+ new_label = main_block_label (label);
+ if (new_label != label)
+ gimple_cond_set_true_label (stmt, new_label);
+ }
- if (true_label)
- gimple_cond_set_true_label (stmt, main_block_label (true_label));
- if (false_label)
- gimple_cond_set_false_label (stmt, main_block_label
(false_label));
- break;
- }
+ label = gimple_cond_false_label (stmt);
+ if (label)
+ {
+ new_label = main_block_label (label);
+ if (new_label != label)
+ gimple_cond_set_false_label (stmt, new_label);
+ }
+ break;
case GIMPLE_SWITCH:
{
@@ -1013,8 +1027,10 @@ cleanup_dead_labels (void)
for (i = 0; i < n; ++i)
{
tree case_label = gimple_switch_label (stmt, i);
- tree label = main_block_label (CASE_LABEL (case_label));
- CASE_LABEL (case_label) = label;
+ label = CASE_LABEL (case_label);
+ new_label = main_block_label (label);
+ if (new_label != label)
+ CASE_LABEL (case_label) = label;
}
break;
}
@@ -1024,10 +1040,24 @@ cleanup_dead_labels (void)
case GIMPLE_GOTO:
if (!computed_goto_p (stmt))
{
- tree new_dest = main_block_label (gimple_goto_dest (stmt));
- gimple_goto_set_dest (stmt, new_dest);
- break;
+ label = gimple_goto_dest (stmt);
+ new_label = main_block_label (label);
+ if (new_label != label)
+ gimple_goto_set_dest (stmt, new_label);
}
+ break;
+
+ case GIMPLE_TM_ATOMIC:
+ {
+ tree label = gimple_tm_atomic_label (stmt);
+ if (label)
+ {
+ tree new_label = main_block_label (label);
+ if (new_label != label)
+ gimple_tm_atomic_set_label (stmt, new_label);
+ }
+ }
+ break;
default:
break;
@@ -2578,12 +2608,14 @@ is_ctrl_altering_stmt (gimple t)
if (is_gimple_omp (t))
return true;
- /* __tm_atomic can alter control flow. */
- if (gimple_code (t) == GIMPLE_TM_ATOMIC)
+ /* If a statement can throw, it alters control flow. */
+ if (stmt_can_throw_internal (t))
return true;
- /* If a statement can throw, it alters control flow. */
- return stmt_can_throw_internal (t);
+ if (flag_tm && is_transactional_stmt (t))
+ return true;
+
+ return false;
}
@@ -4085,12 +4117,15 @@ verify_stmt (gimple_stmt_iterator *gsi)
to match. */
if (lookup_stmt_eh_region (stmt) >= 0)
{
- if (!stmt_could_throw_p (stmt))
+ bool is_tm = is_transactional_stmt (stmt);
+
+ if (!is_tm && !stmt_could_throw_p (stmt))
{
error ("statement marked for throw, but doesn%'t");
goto fail;
}
- if (!last_in_block && stmt_can_throw_internal (stmt))
+ /* ??? Add a transactional function akin to stmt_can_throw_internal. */
+ if (!last_in_block && !is_tm && stmt_can_throw_internal (stmt))
{
error ("statement marked for throw in middle of block");
goto fail;
Index: passes.c
===================================================================
--- passes.c (revision 141199)
+++ passes.c (revision 141200)
@@ -515,6 +515,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_lower_cf);
NEXT_PASS (pass_refactor_eh);
NEXT_PASS (pass_lower_eh);
+ NEXT_PASS (pass_lower_tm);
NEXT_PASS (pass_build_cfg);
NEXT_PASS (pass_lower_complex_O0);
NEXT_PASS (pass_lower_vector);
@@ -540,13 +541,11 @@ init_optimization_passes (void)
NEXT_PASS (pass_cleanup_cfg);
NEXT_PASS (pass_init_datastructures);
NEXT_PASS (pass_expand_omp);
- NEXT_PASS (pass_expand_tm);
NEXT_PASS (pass_referenced_vars);
NEXT_PASS (pass_reset_cc_flags);
NEXT_PASS (pass_build_ssa);
NEXT_PASS (pass_early_warn_uninitialized);
- /* NEXT_PASS (pass_checkpoint_tm); */
NEXT_PASS (pass_all_early_optimizations);
{
struct opt_pass **p = &pass_all_early_optimizations.pass.sub;
@@ -565,6 +564,7 @@ init_optimization_passes (void)
NEXT_PASS (pass_convert_switch);
NEXT_PASS (pass_profile);
}
+ NEXT_PASS (pass_checkpoint_tm);
NEXT_PASS (pass_release_ssa_names);
NEXT_PASS (pass_rebuild_cgraph_edges);
NEXT_PASS (pass_inline_parameters);
Index: c-parser.c
===================================================================
--- c-parser.c (revision 141199)
+++ c-parser.c (revision 141200)
@@ -8255,7 +8255,7 @@ c_parser_tm_abort (c_parser *parser)
/* ??? Verify that __tm_abort is contained within the
lexical scope of a __tm_atomic. */
- return build_call_expr (built_in_decls[BUILT_IN_TM_ABORT], 0);
+ return add_stmt (build_call_expr (built_in_decls[BUILT_IN_TM_ABORT], 0));
}
/* Parse a single source file. */