This patch implements a new internal function that has a 'uniqueness' property. Jump-threading cannot clone it and tail-merging cannot combine multiple instances.

The uniqueness is implemented by a new gimple fn, gimple_call_internal_unique_p. Routines that check for identical or cloneable calls are augmented to check this property. These are:

* tree-ssa-threadedge, which is figuring out if jump threading is a win. Jump threading is inhibited.

* gimple_call_same_target_p, used for tail merging and similar transforms. Two calls of IFN_UNIQUE will never be the same target.

* tracer.c, which is determining whether to clone a region.

Interestingly jump threading avoids cloning volatile asms (which it admits is conservatively safe), but the tracer does not. I wonder if there's a latent problem in tracer?

The reason I needed a function with this property is to preserve the looping structure of a function's CFG. As mentioned in the intro, we mark up loops (using this builtin), so the example I gave has the following inserts:

#pragma acc parallel ...
{
 // single mode here
#pragma acc loop ...
IFN_UNIQUE (FORKING  ...)
for (i = 0; i < N; i++) // loop 1
  ... // partitioned mode here
IFN_UNIQUE (JOINING ...)

if (expr) // single mode here
#pragma acc loop ...
  IFN_UNIQUE (FORKING ...)
  for (i = 0; i < N; i++) // loop 2
    ... // partitioned mode here
  IFN_UNIQUE (JOINING ...)
}

The properly nested loop property of the CFG is preserved through the compilation. This is important as (a) it allows later passes to reconstruct this looping structure and (b) hardware constraints require a partioned region end for all partitioned threads at a single instruction.

Until I added this unique property, original bring-up of partitioned execution would hit cases of split loops ending in multiple cloned JOINING markers and similar cases.

To distinguish different uses of the UNIQUE function, I use the first argument, which is expected to be an INTEGER_CST. I figured this better than using multiple new internal fns, all with the unique property, as the latter would need (at least) a range check in gimple_call_internal_unique_p rather than a simple equality.

Jakub, IYR I originally had IFN_FORK and IFN_JOIN as such distinct internal fns. This replaces that scheme.

ok?

nathan
2015-10-20  Nathan Sidwell  <nat...@codesourcery.com>
	    Cesar Philippidis  <ce...@codesourcery.com>
	
	* internal-fn.c (expand_UNIQUE): New.
	* internal-fn.def (IFN_UNIQUE): New.
	(IFN_UNIQUE_UNSPEC): Define.
	* gimple.h (gimple_call_internal_unique_p): New.
	* gimple.c (gimple_call_same_target_p): Check internal fn
	uniqueness.
	* tracer.c (ignore_bb_p): Check for IFN_UNIQUE call.
	* tree-ssa-threadedge.c
	(record_temporary_equivalences_from_stmts): Likewise.

Index: gimple.c
===================================================================
--- gimple.c	(revision 229096)
+++ gimple.c	(working copy)
@@ -1346,7 +1346,8 @@ gimple_call_same_target_p (const gimple
 {
   if (gimple_call_internal_p (c1))
     return (gimple_call_internal_p (c2)
-	    && gimple_call_internal_fn (c1) == gimple_call_internal_fn (c2));
+	    && gimple_call_internal_fn (c1) == gimple_call_internal_fn (c2)
+	    && !gimple_call_internal_unique_p (as_a <const gcall *> (c1)));
   else
     return (gimple_call_fn (c1) == gimple_call_fn (c2)
 	    || (gimple_call_fndecl (c1)
Index: gimple.h
===================================================================
--- gimple.h	(revision 229096)
+++ gimple.h	(working copy)
@@ -2895,6 +2895,14 @@ gimple_call_internal_fn (const gimple *g
   return gimple_call_internal_fn (gc);
 }
 
+/* Return true, if this internal gimple call is unique.  */
+
+static inline bool
+gimple_call_internal_unique_p (const gcall *gs)
+{
+  return gimple_call_internal_fn (gs) == IFN_UNIQUE;
+}
+
 /* If CTRL_ALTERING_P is true, mark GIMPLE_CALL S to be a stmt
    that could alter control flow.  */
 
Index: internal-fn.c
===================================================================
--- internal-fn.c	(revision 229096)
+++ internal-fn.c	(working copy)
@@ -1958,6 +1958,30 @@ expand_VA_ARG (gcall *stmt ATTRIBUTE_UNU
   gcc_unreachable ();
 }
 
+/* Expand the IFN_UNIQUE function according to its first argument.  */
+
+static void
+expand_UNIQUE (gcall *stmt)
+{
+  rtx pattern = NULL_RTX;
+
+  switch (TREE_INT_CST_LOW (gimple_call_arg (stmt, 0)))
+    {
+    default:
+      gcc_unreachable ();
+      break;
+
+    case IFN_UNIQUE_UNSPEC:
+#ifdef HAVE_unique
+      pattern = gen_unique ();
+#endif
+      break;
+    }
+
+  if (pattern)
+    emit_insn (pattern);
+}
+
 /* Routines to expand each internal function, indexed by function number.
    Each routine has the prototype:
 
Index: internal-fn.def
===================================================================
--- internal-fn.def	(revision 229096)
+++ internal-fn.def	(working copy)
@@ -65,3 +65,11 @@ DEF_INTERNAL_FN (SUB_OVERFLOW, ECF_CONST
 DEF_INTERNAL_FN (MUL_OVERFLOW, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (TSAN_FUNC_EXIT, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (VA_ARG, ECF_NOTHROW | ECF_LEAF, NULL)
+
+/* An unduplicable, uncombinable function.  Generally used to preserve
+   a CFG property in the face of jump threading, tail merging or
+   other such optimizations.  The first argument distinguishes
+   between uses.  Other arguments are as needed for use.  The return
+   type depends on use too.  */
+DEF_INTERNAL_FN (UNIQUE, ECF_NOTHROW | ECF_LEAF, NULL)
+#define IFN_UNIQUE_UNSPEC 0  /* Undifferentiated UNIQUE.  */
Index: tracer.c
===================================================================
--- tracer.c	(revision 229096)
+++ tracer.c	(working copy)
@@ -93,6 +93,7 @@ bb_seen_p (basic_block bb)
 static bool
 ignore_bb_p (const_basic_block bb)
 {
+  gimple_stmt_iterator gsi;
   gimple *g;
 
   if (bb->index < NUM_FIXED_BLOCKS)
@@ -106,6 +107,17 @@ ignore_bb_p (const_basic_block bb)
   if (g && gimple_code (g) == GIMPLE_TRANSACTION)
     return true;
 
+  /* Ignore blocks containing non-clonable function calls.  */
+  for (gsi = gsi_start_bb (CONST_CAST_BB (bb));
+       !gsi_end_p (gsi); gsi_next (&gsi))
+    {
+      g = gsi_stmt (gsi);
+
+      if (is_gimple_call (g) && gimple_call_internal_p (g)
+	  && gimple_call_internal_unique_p (as_a <gcall *> (g)))
+	return true;
+    }
+
   return false;
 }
 
Index: tree-ssa-threadedge.c
===================================================================
--- tree-ssa-threadedge.c	(revision 229096)
+++ tree-ssa-threadedge.c	(working copy)
@@ -283,6 +283,17 @@ record_temporary_equivalences_from_stmts
 	  && gimple_asm_volatile_p (as_a <gasm *> (stmt)))
 	return NULL;
 
+      /* If the statement is a unique builtin, we can not thread
+	 through here.  */
+      if (gimple_code (stmt) == GIMPLE_CALL)
+	{
+	  gcall *call = as_a <gcall *> (stmt);
+
+	  if (gimple_call_internal_p (call)
+	      && gimple_call_internal_unique_p (call))
+	    return NULL;
+	}
+
       /* If duplicating this block is going to cause too much code
 	 expansion, then do not thread through this block.  */
       stmt_count++;

Reply via email to