diff mbox

PR ipa/67811

Message ID 5679AA48.9080404@redhat.com
State New
Headers show

Commit Message

Richard Henderson Dec. 22, 2015, 7:53 p.m. UTC
The problem in this PR is that when we duplicate blocks, we fail
to duplicate eh regions.

Most of the places that we want to do block duplication, that's fine.
We've already identified that we're operating on a SESE region, or
suchlike, and so EH doesn't apply -- if EH were present it wouldn't
be SESE and we'd not attempt the optimization.

Rather than attempt to write a bunch of new code in order to make
this work, I'd prefer to duplicate code early, when it's still just
medium-level GENERIC nodes, and then let the rather better tested
code remove unreachable code and EH regions when necessary.

Tested on x86_64 and committed.


r~
diff mbox

Patch

	* gimple.h (struct gtransaction): Add label_norm, label_uninst;
	replace label with label_over.
	(gimple_build_transaction): Remove label parameter.
	(gimple_transaction_label_norm): New.
	(gimple_transaction_label_uninst): New.
	(gimple_transaction_label_over): Rename from gimple_transaction_label.
	(gimple_transaction_label_norm_ptr): New.
	(gimple_transaction_label_uninst_ptr): New.
	(gimple_transaction_label_over_ptr): Rename from
	gimple_transaction_label_ptr.
	(gimple_transaction_set_label_norm): New.
	(gimple_transaction_set_label_uninst): New.
	(gimple_transaction_set_label_over): Rename from
	gimple_transaction_set_label.
	* gimple-pretty-print.c (dump_gimple_transaction): Update.
	* gimple-streamer-in.c (input_gimple_stmt) [GIMPLE_TRANSACTION]: Same.
	* gimple-streamer-out.c (output_gimple_stmt) [GIMPLE_TRANSACTION]: Same.
	* gimple-walk.c (walk_gimple_op) [GIMPLE_TRANSACTION]: Same.
	* tree-cfg.c (make_edges_bb) [GIMPLE_TRANSACTION]: Same.
	(cleanup_dead_labels) [GIMPLE_TRANSACTION]: Same.
	(verify_gimple_transaction): Same.
	(gimple_redirect_edge_and_branch) [GIMPLE_TRANSACTION]: Same.
	* tree-inline.c (remap_gimple_stmt) [GIMPLE_TRANSACTION]: Same.
	* gimple.c (gimple_build_transaction): Remove label parameter;
	initialize all three label memebers.
	* gimplify.c (gimplify_transaction): Update call
	to gimple_build_transaction.
	* trans-mem.c (make_tm_uninst): New.
	(lower_transaction): Create uninstrumented code path here...
	(ipa_tm_scan_calls_transaction): ... not here.
	(ipa_uninstrument_transaction): Remove.

testsuite/
	* g++.dg/tm/noexcept-1.C: Update expected must_not_throw count.
	* g++.dg/tm/noexcept-4.C: Likewise.
	* g++.dg/tm/noexcept-5.C: Likewise.
	* g++.dg/tm/pr67811.C: New.

git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@231907 138bc75d-0d04-0410-961f-82ee72b054a4
---
 gcc/ChangeLog                        |  35 +++++++++++
 gcc/gimple-pretty-print.c            |  48 +++++++++------
 gcc/gimple-streamer-in.c             |   8 ++-
 gcc/gimple-streamer-out.c            |   8 ++-
 gcc/gimple-walk.c                    |  21 +++++--
 gcc/gimple.c                         |   6 +-
 gcc/gimple.h                         |  59 ++++++++++++++----
 gcc/gimplify.c                       |   2 +-
 gcc/testsuite/ChangeLog              |   7 +++
 gcc/testsuite/g++.dg/tm/noexcept-1.C |   2 +-
 gcc/testsuite/g++.dg/tm/noexcept-4.C |   2 +-
 gcc/testsuite/g++.dg/tm/noexcept-5.C |   2 +-
 gcc/testsuite/g++.dg/tm/pr67811.C    |  11 ++++
 gcc/trans-mem.c                      | 115 ++++++++++++++++++-----------------
 gcc/tree-cfg.c                       |  70 ++++++++++++++++-----
 gcc/tree-inline.c                    |  14 +++--
 16 files changed, 288 insertions(+), 122 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/tm/pr67811.C

diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 11f53c7..a9e73f4 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,38 @@ 
+2015-12-22  Richard Henderson  <rth@redhat.com>
+
+	PR ipa/67811
+	* gimple.h (struct gtransaction): Add label_norm, label_uninst;
+	replace label with label_over.
+	(gimple_build_transaction): Remove label parameter.
+	(gimple_transaction_label_norm): New.
+	(gimple_transaction_label_uninst): New.
+	(gimple_transaction_label_over): Rename from gimple_transaction_label.
+	(gimple_transaction_label_norm_ptr): New.
+	(gimple_transaction_label_uninst_ptr): New.
+	(gimple_transaction_label_over_ptr): Rename from
+	gimple_transaction_label_ptr.
+	(gimple_transaction_set_label_norm): New.
+	(gimple_transaction_set_label_uninst): New.
+	(gimple_transaction_set_label_over): Rename from
+	gimple_transaction_set_label.
+	* gimple-pretty-print.c (dump_gimple_transaction): Update.
+	* gimple-streamer-in.c (input_gimple_stmt) [GIMPLE_TRANSACTION]: Same.
+	* gimple-streamer-out.c (output_gimple_stmt) [GIMPLE_TRANSACTION]: Same.
+	* gimple-walk.c (walk_gimple_op) [GIMPLE_TRANSACTION]: Same.
+	* tree-cfg.c (make_edges_bb) [GIMPLE_TRANSACTION]: Same.
+	(cleanup_dead_labels) [GIMPLE_TRANSACTION]: Same.
+	(verify_gimple_transaction): Same.
+	(gimple_redirect_edge_and_branch) [GIMPLE_TRANSACTION]: Same.
+	* tree-inline.c (remap_gimple_stmt) [GIMPLE_TRANSACTION]: Same.
+	* gimple.c (gimple_build_transaction): Remove label parameter;
+	initialize all three label memebers.
+	* gimplify.c (gimplify_transaction): Update call
+	to gimple_build_transaction.
+	* trans-mem.c (make_tm_uninst): New.
+	(lower_transaction): Create uninstrumented code path here...
+	(ipa_tm_scan_calls_transaction): ... not here.
+	(ipa_uninstrument_transaction): Remove.
+
 2015-12-22  Peter Bergner  <bergner@vnet.ibm.com>
 
 	PR target/68772
diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 01e9b6b..41e4c5e 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -1607,8 +1607,11 @@  dump_gimple_transaction (pretty_printer *buffer, gtransaction *gs,
   if (flags & TDF_RAW)
     {
       dump_gimple_fmt (buffer, spc, flags,
-		       "%G [SUBCODE=%x,LABEL=%T] <%+BODY <%S> >",
-		       gs, subcode, gimple_transaction_label (gs),
+		       "%G [SUBCODE=%x,NORM=%T,UNINST=%T,OVER=%T] "
+		       "<%+BODY <%S> >",
+		       gs, subcode, gimple_transaction_label_norm (gs),
+		       gimple_transaction_label_uninst (gs),
+		       gimple_transaction_label_over (gs),
 		       gimple_transaction_body (gs));
     }
   else
@@ -1621,13 +1624,35 @@  dump_gimple_transaction (pretty_printer *buffer, gtransaction *gs,
 	pp_string (buffer, "__transaction_atomic");
       subcode &= ~GTMA_DECLARATION_MASK;
 
-      if (subcode || gimple_transaction_label (gs))
+      if (gimple_transaction_body (gs))
+	{
+	  newline_and_indent (buffer, spc + 2);
+	  pp_left_brace (buffer);
+	  pp_newline (buffer);
+	  dump_gimple_seq (buffer, gimple_transaction_body (gs),
+			   spc + 4, flags);
+	  newline_and_indent (buffer, spc + 2);
+	  pp_right_brace (buffer);
+	}
+      else
 	{
 	  pp_string (buffer, "  //");
-	  if (gimple_transaction_label (gs))
+	  if (gimple_transaction_label_norm (gs))
+	    {
+	      pp_string (buffer, " NORM=");
+	      dump_generic_node (buffer, gimple_transaction_label_norm (gs),
+				 spc, flags, false);
+	    }
+	  if (gimple_transaction_label_uninst (gs))
 	    {
-	      pp_string (buffer, " LABEL=");
-	      dump_generic_node (buffer, gimple_transaction_label (gs),
+	      pp_string (buffer, " UNINST=");
+	      dump_generic_node (buffer, gimple_transaction_label_uninst (gs),
+				 spc, flags, false);
+	    }
+	  if (gimple_transaction_label_over (gs))
+	    {
+	      pp_string (buffer, " OVER=");
+	      dump_generic_node (buffer, gimple_transaction_label_over (gs),
 				 spc, flags, false);
 	    }
 	  if (subcode)
@@ -1668,17 +1693,6 @@  dump_gimple_transaction (pretty_printer *buffer, gtransaction *gs,
 	      pp_right_bracket (buffer);
 	    }
 	}
-
-      if (!gimple_seq_empty_p (gimple_transaction_body (gs)))
-	{
-	  newline_and_indent (buffer, spc + 2);
-	  pp_left_brace (buffer);
-	  pp_newline (buffer);
-	  dump_gimple_seq (buffer, gimple_transaction_body (gs),
-			   spc + 4, flags);
-	  newline_and_indent (buffer, spc + 2);
-	  pp_right_brace (buffer);
-	}
     }
 }
 
diff --git a/gcc/gimple-streamer-in.c b/gcc/gimple-streamer-in.c
index 44bcc11..6310717 100644
--- a/gcc/gimple-streamer-in.c
+++ b/gcc/gimple-streamer-in.c
@@ -198,8 +198,12 @@  input_gimple_stmt (struct lto_input_block *ib, struct data_in *data_in,
       break;
 
     case GIMPLE_TRANSACTION:
-      gimple_transaction_set_label (as_a <gtransaction *> (stmt),
-				    stream_read_tree (ib, data_in));
+      gimple_transaction_set_label_norm (as_a <gtransaction *> (stmt),
+				         stream_read_tree (ib, data_in));
+      gimple_transaction_set_label_uninst (as_a <gtransaction *> (stmt),
+				           stream_read_tree (ib, data_in));
+      gimple_transaction_set_label_over (as_a <gtransaction *> (stmt),
+				         stream_read_tree (ib, data_in));
       break;
 
     default:
diff --git a/gcc/gimple-streamer-out.c b/gcc/gimple-streamer-out.c
index 1431f5c..cbcb4db 100644
--- a/gcc/gimple-streamer-out.c
+++ b/gcc/gimple-streamer-out.c
@@ -180,9 +180,11 @@  output_gimple_stmt (struct output_block *ob, gimple *stmt)
 
     case GIMPLE_TRANSACTION:
       {
-	gtransaction *trans_stmt = as_a <gtransaction *> (stmt);
-	gcc_assert (gimple_transaction_body (trans_stmt) == NULL);
-	stream_write_tree (ob, gimple_transaction_label (trans_stmt), true);
+	gtransaction *txn = as_a <gtransaction *> (stmt);
+	gcc_assert (gimple_transaction_body (txn) == NULL);
+	stream_write_tree (ob, gimple_transaction_label_norm (txn), true);
+	stream_write_tree (ob, gimple_transaction_label_uninst (txn), true);
+	stream_write_tree (ob, gimple_transaction_label_over (txn), true);
       }
       break;
 
diff --git a/gcc/gimple-walk.c b/gcc/gimple-walk.c
index 850cf57..b8bae50 100644
--- a/gcc/gimple-walk.c
+++ b/gcc/gimple-walk.c
@@ -474,11 +474,22 @@  walk_gimple_op (gimple *stmt, walk_tree_fn callback_op,
       break;
 
     case GIMPLE_TRANSACTION:
-      ret = walk_tree (gimple_transaction_label_ptr (
-			 as_a <gtransaction *> (stmt)),
-		       callback_op, wi, pset);
-      if (ret)
-	return ret;
+      {
+	gtransaction *txn = as_a <gtransaction *> (stmt);
+
+	ret = walk_tree (gimple_transaction_label_norm_ptr (txn),
+			 callback_op, wi, pset);
+	if (ret)
+	  return ret;
+	ret = walk_tree (gimple_transaction_label_uninst_ptr (txn),
+			 callback_op, wi, pset);
+	if (ret)
+	  return ret;
+	ret = walk_tree (gimple_transaction_label_over_ptr (txn),
+			 callback_op, wi, pset);
+	if (ret)
+	  return ret;
+      }
       break;
 
     case GIMPLE_OMP_RETURN:
diff --git a/gcc/gimple.c b/gcc/gimple.c
index bf552a7..49663df 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -1131,12 +1131,14 @@  gimple_build_omp_atomic_store (tree val)
 /* Build a GIMPLE_TRANSACTION statement.  */
 
 gtransaction *
-gimple_build_transaction (gimple_seq body, tree label)
+gimple_build_transaction (gimple_seq body)
 {
   gtransaction *p
     = as_a <gtransaction *> (gimple_alloc (GIMPLE_TRANSACTION, 0));
   gimple_transaction_set_body (p, body);
-  gimple_transaction_set_label (p, label);
+  gimple_transaction_set_label_norm (p, 0);
+  gimple_transaction_set_label_uninst (p, 0);
+  gimple_transaction_set_label_over (p, 0);
   return p;
 }
 
diff --git a/gcc/gimple.h b/gcc/gimple.h
index dc61043..adfe071 100644
--- a/gcc/gimple.h
+++ b/gcc/gimple.h
@@ -836,8 +836,10 @@  struct GTY((tag("GSS_TRANSACTION")))
   /* [ WORD 10 ] */
   gimple_seq body;
 
-  /* [ WORD 11 ] */
-  tree label;
+  /* [ WORD 11-13 ] */
+  tree label_norm;
+  tree label_uninst;
+  tree label_over;
 };
 
 #define DEFGSSTRUCT(SYM, STRUCT, HAS_TREE_OP)	SYM,
@@ -1463,7 +1465,7 @@  gomp_target *gimple_build_omp_target (gimple_seq, int, tree);
 gomp_teams *gimple_build_omp_teams (gimple_seq, tree);
 gomp_atomic_load *gimple_build_omp_atomic_load (tree, tree);
 gomp_atomic_store *gimple_build_omp_atomic_store (tree);
-gtransaction *gimple_build_transaction (gimple_seq, tree);
+gtransaction *gimple_build_transaction (gimple_seq);
 extern void gimple_seq_add_stmt (gimple_seq *, gimple *);
 extern void gimple_seq_add_stmt_without_update (gimple_seq *, gimple *);
 void gimple_seq_add_seq (gimple_seq *, gimple_seq);
@@ -5847,21 +5849,45 @@  gimple_transaction_body_ptr (gtransaction *transaction_stmt)
 static inline gimple_seq
 gimple_transaction_body (gtransaction *transaction_stmt)
 {
-  return *gimple_transaction_body_ptr (transaction_stmt);
+  return transaction_stmt->body;
 }
 
 /* Return the label associated with a GIMPLE_TRANSACTION.  */
 
 static inline tree
-gimple_transaction_label (const gtransaction *transaction_stmt)
+gimple_transaction_label_norm (const gtransaction *transaction_stmt)
 {
-  return transaction_stmt->label;
+  return transaction_stmt->label_norm;
 }
 
 static inline tree *
-gimple_transaction_label_ptr (gtransaction *transaction_stmt)
+gimple_transaction_label_norm_ptr (gtransaction *transaction_stmt)
 {
-  return &transaction_stmt->label;
+  return &transaction_stmt->label_norm;
+}
+
+static inline tree
+gimple_transaction_label_uninst (const gtransaction *transaction_stmt)
+{
+  return transaction_stmt->label_uninst;
+}
+
+static inline tree *
+gimple_transaction_label_uninst_ptr (gtransaction *transaction_stmt)
+{
+  return &transaction_stmt->label_uninst;
+}
+
+static inline tree
+gimple_transaction_label_over (const gtransaction *transaction_stmt)
+{
+  return transaction_stmt->label_over;
+}
+
+static inline tree *
+gimple_transaction_label_over_ptr (gtransaction *transaction_stmt)
+{
+  return &transaction_stmt->label_over;
 }
 
 /* Return the subcode associated with a GIMPLE_TRANSACTION.  */
@@ -5885,9 +5911,21 @@  gimple_transaction_set_body (gtransaction *transaction_stmt,
 /* Set the label associated with a GIMPLE_TRANSACTION.  */
 
 static inline void
-gimple_transaction_set_label (gtransaction *transaction_stmt, tree label)
+gimple_transaction_set_label_norm (gtransaction *transaction_stmt, tree label)
+{
+  transaction_stmt->label_norm = label;
+}
+
+static inline void
+gimple_transaction_set_label_uninst (gtransaction *transaction_stmt, tree label)
+{
+  transaction_stmt->label_uninst = label;
+}
+
+static inline void
+gimple_transaction_set_label_over (gtransaction *transaction_stmt, tree label)
 {
-  transaction_stmt->label = label;
+  transaction_stmt->label_over = label;
 }
 
 /* Set the subcode associated with a GIMPLE_TRANSACTION.  */
@@ -5899,7 +5937,6 @@  gimple_transaction_set_subcode (gtransaction *transaction_stmt,
   transaction_stmt->subcode = subcode;
 }
 
-
 /* Return a pointer to the return value for GIMPLE_RETURN GS.  */
 
 static inline tree *
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 438efba..62b0e64 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -9739,7 +9739,7 @@  gimplify_transaction (tree *expr_p, gimple_seq *pre_p)
   body_stmt = gimplify_and_return_first (TRANSACTION_EXPR_BODY (expr), &body);
   pop_gimplify_context (body_stmt);
 
-  trans_stmt = gimple_build_transaction (body, NULL);
+  trans_stmt = gimple_build_transaction (body);
   if (TRANSACTION_EXPR_OUTER (expr))
     subcode = GTMA_IS_OUTER;
   else if (TRANSACTION_EXPR_RELAXED (expr))
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 19fccfd..3c2e2ac 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,10 @@ 
+2015-12-22  Richard Henderson  <rth@redhat.com>
+
+	* g++.dg/tm/noexcept-1.C: Update expected must_not_throw count.
+	* g++.dg/tm/noexcept-4.C: Likewise.
+	* g++.dg/tm/noexcept-5.C: Likewise.
+	* g++.dg/tm/pr67811.C: New.
+
 2015-12-22  Peter Bergner  <bergner@vnet.ibm.com>
 
 	PR target/68772
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-1.C b/gcc/testsuite/g++.dg/tm/noexcept-1.C
index bae1bea..dcbd3d8 100644
--- a/gcc/testsuite/g++.dg/tm/noexcept-1.C
+++ b/gcc/testsuite/g++.dg/tm/noexcept-1.C
@@ -32,5 +32,5 @@  int f3()
   return global;
 }
 
-/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 6 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 12 "tmlower" } } */
 /* { dg-final { scan-tree-dump-times "ITM_RU" 6 "tmmark" } } */
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-4.C b/gcc/testsuite/g++.dg/tm/noexcept-4.C
index af7cd8d..82db6ab 100644
--- a/gcc/testsuite/g++.dg/tm/noexcept-4.C
+++ b/gcc/testsuite/g++.dg/tm/noexcept-4.C
@@ -29,5 +29,5 @@  int f3()
          + __transaction_atomic noexcept (global + 6);
 }
 
-/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 6 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 12 "tmlower" } } */
 /* { dg-final { scan-tree-dump-times "ITM_RU" 6 "tmmark" } } */
diff --git a/gcc/testsuite/g++.dg/tm/noexcept-5.C b/gcc/testsuite/g++.dg/tm/noexcept-5.C
index 03aba5a..7744bfb 100644
--- a/gcc/testsuite/g++.dg/tm/noexcept-5.C
+++ b/gcc/testsuite/g++.dg/tm/noexcept-5.C
@@ -15,5 +15,5 @@  void f2(int x)
         }
     }
 }
-/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 1 "tmlower" } } */
+/* { dg-final { scan-tree-dump-times "eh_must_not_throw" 2 "tmlower" } } */
 /* { dg-final { scan-tree-dump-times "ITM_RU" 1 "tmmark" } } */
diff --git a/gcc/testsuite/g++.dg/tm/pr67811.C b/gcc/testsuite/g++.dg/tm/pr67811.C
new file mode 100644
index 0000000..90ab64c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/tm/pr67811.C
@@ -0,0 +1,11 @@ 
+// { dg-do compile }
+// { dg-options "-fgnu-tm" }
+
+void f()
+{
+  __transaction_relaxed {
+    try { throw 42; }
+    catch (...) { }
+  }
+}
+
diff --git a/gcc/trans-mem.c b/gcc/trans-mem.c
index 4583bd5..fa092f1 100644
--- a/gcc/trans-mem.c
+++ b/gcc/trans-mem.c
@@ -1608,6 +1608,27 @@  examine_call_tm (unsigned *state, gimple_stmt_iterator *gsi)
   *state |= GTMA_HAVE_LOAD | GTMA_HAVE_STORE;
 }
 
+/* Iterate through the statements in the sequence, moving labels
+   (and thus edges) of transactions from "label_norm" to "label_uninst".  */
+
+static tree
+make_tm_uninst (gimple_stmt_iterator *gsi, bool *handled_ops_p,
+                struct walk_stmt_info *)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+
+  if (gtransaction *txn = dyn_cast <gtransaction *> (stmt))
+    {
+      *handled_ops_p = true;
+      txn->label_uninst = txn->label_norm;
+      txn->label_norm = NULL;
+    }
+  else
+    *handled_ops_p = !gimple_has_substatements (stmt);
+
+  return NULL_TREE;
+}
+
 /* Lower a GIMPLE_TRANSACTION statement.  */
 
 static void
@@ -1670,20 +1691,49 @@  lower_transaction (gimple_stmt_iterator *gsi, struct walk_stmt_info *wi)
 
   g = gimple_build_try (gimple_transaction_body (stmt),
 			gimple_seq_alloc_with_stmt (g), GIMPLE_TRY_FINALLY);
-  gsi_insert_after (gsi, g, GSI_CONTINUE_LINKING);
 
-  gimple_transaction_set_body (stmt, NULL);
+  /* For a (potentially) outer transaction, create two paths.  */
+  gimple_seq uninst = NULL;
+  if (outer_state == NULL)
+    {
+      uninst = copy_gimple_seq_and_replace_locals (g);
+      /* In the uninstrumented copy, reset inner transactions to have only
+	 an uninstrumented code path.  */
+      memset (&this_wi, 0, sizeof (this_wi));
+      walk_gimple_seq (uninst, make_tm_uninst, NULL, &this_wi);
+    }
+
+  tree label1 = create_artificial_label (UNKNOWN_LOCATION);
+  gsi_insert_after (gsi, gimple_build_label (label1), GSI_CONTINUE_LINKING);
+  gsi_insert_after (gsi, g, GSI_CONTINUE_LINKING);
+  gimple_transaction_set_label_norm (stmt, label1);
 
   /* If the transaction calls abort or if this is an outer transaction,
      add an "over" label afterwards.  */
-  if ((this_state & (GTMA_HAVE_ABORT))
+  tree label3 = NULL;
+  if ((this_state & GTMA_HAVE_ABORT)
+      || outer_state == NULL
       || (gimple_transaction_subcode (stmt) & GTMA_IS_OUTER))
     {
-      tree label = create_artificial_label (UNKNOWN_LOCATION);
-      gimple_transaction_set_label (stmt, label);
-      gsi_insert_after (gsi, gimple_build_label (label), GSI_CONTINUE_LINKING);
+      label3 = create_artificial_label (UNKNOWN_LOCATION);
+      gimple_transaction_set_label_over (stmt, label3);
     }
 
+  if (uninst != NULL)
+    {
+      gsi_insert_after (gsi, gimple_build_goto (label3), GSI_CONTINUE_LINKING);
+
+      tree label2 = create_artificial_label (UNKNOWN_LOCATION);
+      gsi_insert_after (gsi, gimple_build_label (label2), GSI_CONTINUE_LINKING);
+      gsi_insert_seq_after (gsi, uninst, GSI_CONTINUE_LINKING);
+      gimple_transaction_set_label_uninst (stmt, label2);
+    }
+
+  if (label3 != NULL)
+    gsi_insert_after (gsi, gimple_build_label (label3), GSI_CONTINUE_LINKING);
+
+  gimple_transaction_set_body (stmt, NULL);
+
   /* Record the set of operations found for use later.  */
   this_state |= gimple_transaction_subcode (stmt) & GTMA_DECLARATION_MASK;
   gimple_transaction_set_subcode (stmt, this_state);
@@ -4113,35 +4163,6 @@  maybe_push_queue (struct cgraph_node *node,
     }
 }
 
-/* Duplicate the basic blocks in QUEUE for use in the uninstrumented
-   code path.  QUEUE are the basic blocks inside the transaction
-   represented in REGION.
-
-   Later in split_code_paths() we will add the conditional to choose
-   between the two alternatives.  */
-
-static void
-ipa_uninstrument_transaction (struct tm_region *region,
-			      vec<basic_block> queue)
-{
-  gimple *transaction = region->transaction_stmt;
-  basic_block transaction_bb = gimple_bb (transaction);
-  int n = queue.length ();
-  basic_block *new_bbs = XNEWVEC (basic_block, n);
-
-  copy_bbs (queue.address (), n, new_bbs, NULL, 0, NULL, NULL, transaction_bb,
-	    true);
-  edge e = make_edge (transaction_bb, new_bbs[0], EDGE_TM_UNINSTRUMENTED);
-  add_phi_args_after_copy (new_bbs, n, e);
-
-  // Now we will have a GIMPLE_ATOMIC with 3 possible edges out of it.
-  //   a) EDGE_FALLTHRU into the transaction
-  //   b) EDGE_TM_ABORT out of the transaction
-  //   c) EDGE_TM_UNINSTRUMENTED into the uninstrumented blocks.
-
-  free (new_bbs);
-}
-
 /* A subroutine of ipa_tm_scan_calls_transaction and ipa_tm_scan_calls_clone.
    Queue all callees within block BB.  */
 
@@ -4189,43 +4210,23 @@  static void
 ipa_tm_scan_calls_transaction (struct tm_ipa_cg_data *d,
 			       cgraph_node_queue *callees_p)
 {
-  struct tm_region *r;
-
   d->transaction_blocks_normal = BITMAP_ALLOC (&tm_obstack);
   d->all_tm_regions = all_tm_regions;
 
-  for (r = all_tm_regions; r; r = r->next)
+  for (tm_region *r = all_tm_regions; r; r = r->next)
     {
       vec<basic_block> bbs;
       basic_block bb;
       unsigned i;
 
       bbs = get_tm_region_blocks (r->entry_block, r->exit_blocks, NULL,
-				  d->transaction_blocks_normal, false);
-
-      // Generate the uninstrumented code path for this transaction.
-      ipa_uninstrument_transaction (r, bbs);
+				  d->transaction_blocks_normal, false, false);
 
       FOR_EACH_VEC_ELT (bbs, i, bb)
 	ipa_tm_scan_calls_block (callees_p, bb, false);
 
       bbs.release ();
     }
-
-  // ??? copy_bbs should maintain cgraph edges for the blocks as it is
-  // copying them, rather than forcing us to do this externally.
-  cgraph_edge::rebuild_edges ();
-
-  // ??? In ipa_uninstrument_transaction we don't try to update dominators
-  // because copy_bbs doesn't return a VEC like iterate_fix_dominators expects.
-  // Instead, just release dominators here so update_ssa recomputes them.
-  free_dominance_info (CDI_DOMINATORS);
-
-  // When building the uninstrumented code path, copy_bbs will have invoked
-  // create_new_def_for starting an "ssa update context".  There is only one
-  // instance of this context, so resolve ssa updates before moving on to
-  // the next function.
-  update_ssa (TODO_update_ssa);
 }
 
 /* Scan all calls in NODE as if this is the transactional clone,
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index 5aad9ee..436ea14 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -828,11 +828,21 @@  make_edges_bb (basic_block bb, struct omp_region **pcur_region, int *pomp_index)
 
     case GIMPLE_TRANSACTION:
       {
-	tree abort_label
-	  = gimple_transaction_label (as_a <gtransaction *> (last));
-	if (abort_label)
-	  make_edge (bb, label_to_block (abort_label), EDGE_TM_ABORT);
-	fallthru = true;
+        gtransaction *txn = as_a <gtransaction *> (last);
+	tree label1 = gimple_transaction_label_norm (txn);
+	tree label2 = gimple_transaction_label_uninst (txn);
+
+	if (label1)
+	  make_edge (bb, label_to_block (label1), EDGE_FALLTHRU);
+	if (label2)
+	  make_edge (bb, label_to_block (label2),
+		     EDGE_TM_UNINSTRUMENTED | (label1 ? 0 : EDGE_FALLTHRU));
+
+	tree label3 = gimple_transaction_label_over (txn);
+	if (gimple_transaction_subcode (txn) & GTMA_HAVE_ABORT)
+	  make_edge (bb, label_to_block (label3), EDGE_TM_ABORT);
+
+	fallthru = false;
       }
       break;
 
@@ -1517,13 +1527,30 @@  cleanup_dead_labels (void)
 
 	case GIMPLE_TRANSACTION:
 	  {
-	    gtransaction *trans_stmt = as_a <gtransaction *> (stmt);
-	    tree label = gimple_transaction_label (trans_stmt);
+	    gtransaction *txn = as_a <gtransaction *> (stmt);
+
+	    label = gimple_transaction_label_norm (txn);
+	    if (label)
+	      {
+		new_label = main_block_label (label);
+		if (new_label != label)
+		  gimple_transaction_set_label_norm (txn, new_label);
+	      }
+
+	    label = gimple_transaction_label_uninst (txn);
+	    if (label)
+	      {
+		new_label = main_block_label (label);
+		if (new_label != label)
+		  gimple_transaction_set_label_uninst (txn, new_label);
+	      }
+
+	    label = gimple_transaction_label_over (txn);
 	    if (label)
 	      {
-		tree new_label = main_block_label (label);
+		new_label = main_block_label (label);
 		if (new_label != label)
-		  gimple_transaction_set_label (trans_stmt, new_label);
+		  gimple_transaction_set_label_over (txn, new_label);
 	      }
 	  }
 	  break;
@@ -4732,9 +4759,18 @@  verify_gimple_in_seq_2 (gimple_seq stmts)
 static bool
 verify_gimple_transaction (gtransaction *stmt)
 {
-  tree lab = gimple_transaction_label (stmt);
+  tree lab;
+
+  lab = gimple_transaction_label_norm (stmt);
   if (lab != NULL && TREE_CODE (lab) != LABEL_DECL)
     return true;
+  lab = gimple_transaction_label_uninst (stmt);
+  if (lab != NULL && TREE_CODE (lab) != LABEL_DECL)
+    return true;
+  lab = gimple_transaction_label_over (stmt);
+  if (lab != NULL && TREE_CODE (lab) != LABEL_DECL)
+    return true;
+
   return verify_gimple_in_seq_2 (gimple_transaction_body (stmt));
 }
 
@@ -5642,11 +5678,15 @@  gimple_redirect_edge_and_branch (edge e, basic_block dest)
       break;
 
     case GIMPLE_TRANSACTION:
-      /* The ABORT edge has a stored label associated with it, otherwise
-	 the edges are simply redirectable.  */
-      if (e->flags == 0)
-	gimple_transaction_set_label (as_a <gtransaction *> (stmt),
-				      gimple_block_label (dest));
+      if (e->flags & EDGE_TM_ABORT)
+	gimple_transaction_set_label_over (as_a <gtransaction *> (stmt),
+				           gimple_block_label (dest));
+      else if (e->flags & EDGE_TM_UNINSTRUMENTED)
+	gimple_transaction_set_label_uninst (as_a <gtransaction *> (stmt),
+				             gimple_block_label (dest));
+      else
+	gimple_transaction_set_label_norm (as_a <gtransaction *> (stmt),
+				           gimple_block_label (dest));
       break;
 
     default:
diff --git a/gcc/tree-inline.c b/gcc/tree-inline.c
index 36c9622..88a6753 100644
--- a/gcc/tree-inline.c
+++ b/gcc/tree-inline.c
@@ -1518,13 +1518,15 @@  remap_gimple_stmt (gimple *stmt, copy_body_data *id)
 	    gtransaction *new_trans_stmt;
 	    s1 = remap_gimple_seq (gimple_transaction_body (old_trans_stmt),
 				   id);
-	    copy = new_trans_stmt
-	      = gimple_build_transaction (
-		  s1,
-		  gimple_transaction_label (old_trans_stmt));
-	    gimple_transaction_set_subcode (
-              new_trans_stmt,
+	    copy = new_trans_stmt = gimple_build_transaction (s1);
+	    gimple_transaction_set_subcode (new_trans_stmt,
 	      gimple_transaction_subcode (old_trans_stmt));
+	    gimple_transaction_set_label_norm (new_trans_stmt,
+	      gimple_transaction_label_norm (old_trans_stmt));
+	    gimple_transaction_set_label_uninst (new_trans_stmt,
+	      gimple_transaction_label_uninst (old_trans_stmt));
+	    gimple_transaction_set_label_over (new_trans_stmt,
+	      gimple_transaction_label_over (old_trans_stmt));
 	  }
 	  break;
 
-- 
2.5.0