Patchwork [trans-mem] PR46270: handle throwing statements

login
register
mail settings
Submitter Aldy Hernandez
Date Nov. 19, 2010, 4 p.m.
Message ID <4CE69F0B.7080908@redhat.com>
Download mbox | patch
Permalink /patch/72263/
State New
Headers show

Comments

Aldy Hernandez - Nov. 19, 2010, 4 p.m.
In this PR we have a call which may throw inside of a relaxed transaction:

<bb 3>
   it = std::list<std::_List_iterator<Game::BuildProject>  >::begin (&erasableBuildProjects); [in atomic]
<bb 4>
   stuff

Throwable statements can only appear at the end of a basic block, so any instrumentation added to this call, will cause it to throw inside of a basic block and wreck havoc.

Enter ugly code...

We need to split the basic block to keep the throwing statement at the end of the basic block.  This means keeping the normal and abnormal edges sane, and not getting confused by the new BB we will add midstream.  I tried caching the basic blocks ahead of time for the clone case, but noticed that the way gsi_insert_on_edge_immediate() works, the instrumentation will end up in an existing BB and we will instrument already instrumented code:

<bb 3>
   call x
<bb 4>
   instrumentation for call x
   stuff

So what I did was put the instrumentation in BB4 by itself, and mark it to be skipped later.  So we will generate something like:

<bb 3>
   call x
<bb 4>
   instrumentation for call x
<bb 999>
   stuff

Collapsing the code in execute_tm_mark() was not strictly necessary, but I'm getting tired of duplicating code everywhere.

Tested on x86-64 Linux.

OK for branch?
PR/46270
	* trans-mem.c (expand_call_tm): Handle throwing calls.
	(execute_tm_mark): Handle clones and transactions the same.
	Do not expand blocks that are marked as handled.
Richard Henderson - Nov. 19, 2010, 4:26 p.m.
> +      if (!gimple_call_nothrow_p (stmt)
> +	  && gsi_one_before_end_p (*gsi))

As I said before: stmt_can_throw_internal.  You're doing
unnecessary splitting in the case the call *can* throw,
but there's no handler within the current function.

> +	  gsi_insert_on_edge_immediate (fallthru_edge, stmt);
> +
> +	  /* Split the block so the instrumentation code resides in
> +	     it's own separate BB.  Then we can mark this BB as handled
> +	     and avoid rescanning it again.  */
> +	  bb = gimple_bb (stmt);
> +	  split_block (bb, stmt);
> +	  *gsi = gsi_start_bb (bb);

Splitting the block is pointless.  If you don't want to process
the statement, and you're worried about it being placed into a
block that you've not yet processed, then use the non-immediate
form of edge insertion and then use gsi_commit_edge_inserts at
the end.

Normally when we do that, we also accumulate a flag that says
whether calling gsi_commit_edge_inserts is necessary.


r~

Patch

Index: testsuite/g++.dg/tm/pr46270.C
===================================================================
--- testsuite/g++.dg/tm/pr46270.C	(revision 0)
+++ testsuite/g++.dg/tm/pr46270.C	(revision 0)
@@ -0,0 +1,27 @@ 
+// { dg-do compile }
+// { dg-options "-fgnu-tm" }
+
+#include <list>
+class Game
+{
+public:
+  struct BuildProject
+  {
+    int posX;
+  };
+  std::list<BuildProject> buildProjects;
+};
+
+static Game game;
+static std::list<std::list<Game::BuildProject>::iterator> erasableBuildProjects;
+
+static void *buildProjectSyncStepConcurrently(int id, int localTeam)
+{
+  __transaction [[relaxed]] {
+    std::list<std::list<Game::BuildProject>::iterator>::iterator it
+      = erasableBuildProjects.begin();
+    game.buildProjects.erase( (std::list<Game::BuildProject> 
+			       ::iterator) *it);
+  }
+  return 0;
+}
Index: trans-mem.c
===================================================================
--- trans-mem.c	(revision 166872)
+++ trans-mem.c	(working copy)
@@ -2173,12 +2173,52 @@  expand_call_tm (struct tm_region *region
     {
       tree tmp = make_rename_temp (TREE_TYPE (lhs), NULL);
       location_t loc = gimple_location (stmt);
+      edge fallthru_edge = NULL;
+
+      /* Remember if the call was going to throw.  */
+      if (!gimple_call_nothrow_p (stmt)
+	  && gsi_one_before_end_p (*gsi))
+	{
+	  edge_iterator ei;
+	  edge e;
+	  basic_block bb = gimple_bb (stmt);
+
+	  FOR_EACH_EDGE (e, ei, bb->succs)
+	    if (e->flags & EDGE_FALLTHRU)
+	      {
+		fallthru_edge = e;
+		break;
+	      }
+	}
 
       gimple_call_set_lhs (stmt, tmp);
       update_stmt (stmt);
       stmt = gimple_build_assign (lhs, tmp);
       gimple_set_location (stmt, loc);
-      gsi_insert_after (gsi, stmt, GSI_CONTINUE_LINKING);
+
+      /* We cannot throw in the middle of a BB.  If the call was going
+	 to throw, split the block so the call remains the last
+	 statement in the block after we add instrumentation.  */
+      if (fallthru_edge)
+	{
+	  basic_block bb;
+
+	  gsi_insert_on_edge_immediate (fallthru_edge, stmt);
+
+	  /* Split the block so the instrumentation code resides in
+	     it's own separate BB.  Then we can mark this BB as handled
+	     and avoid rescanning it again.  */
+	  bb = gimple_bb (stmt);
+	  split_block (bb, stmt);
+	  *gsi = gsi_start_bb (bb);
+
+	  /* Mark BB as completely handled to avoid expanding it
+	     again, thus creating useless tm_save logs.  */
+	  bb->aux = (void *) true;
+	}
+      else
+	gsi_insert_after (gsi, stmt, GSI_CONTINUE_LINKING);
+
       expand_assign_tm (region, gsi);
 
       transaction_subcode_ior (region, GTMA_HAVE_STORE);
@@ -2304,28 +2344,25 @@  execute_tm_mark (void)
 	  else
 	    subcode &= GTMA_DECLARATION_MASK;
 	  gimple_transaction_set_subcode (region->transaction_stmt, subcode);
-
-	  queue = get_tm_region_blocks (region->entry_block,
-					region->exit_blocks,
-					region->irr_blocks,
-					/*stop_at_irr_p=*/true);
-	  for (i = 0; VEC_iterate (basic_block, queue, i, bb); ++i)
-	    expand_block_tm (region, bb);
-	  VEC_free (basic_block, heap, queue);
 	}
-      else
-	/* ...otherwise, we're a clone and the entire function is the
-	   region.  */
+
+      queue = get_tm_region_blocks (region->entry_block,
+				    region->exit_blocks,
+				    region->irr_blocks,
+				    /*stop_at_irr_p=*/true);
+      for (i = 0; VEC_iterate (basic_block, queue, i, bb); ++i)
 	{
-	  FOR_EACH_BB (bb)
-	    {
-	      /* Stop at irrevocable blocks.  */
-	      if (region->irr_blocks
-		  && bitmap_bit_p (region->irr_blocks, bb->index))
-		break;
-	      expand_block_tm (region, bb);
-	    }
+	  /* In expand_block_tm, we set the BB->AUX field for any
+	     block which we have already handled, to avoid rescanning
+	     it.  This happens in expand_call_tm() on any new BB
+	     inserted which only contains instrumentation code.  */
+	  if (bb->aux == NULL)
+	    expand_block_tm (region, bb);
+	  else
+	    bb->aux = NULL;
 	}
+      VEC_free (basic_block, heap, queue);
+
       tm_log_emit ();
     }