This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

PATCH RFA: PR 29286: Handle placement new aliasing issues


After 175 comments on PR 29286, I think we finally have a patch which
is ready.

The basic issue is how to handle aliasing issues introduced by C++
placement new.  placement new dynamically changes the type of the
memory location to which it is applied.  That means that type based
alias analysis can not be applied to pointers which are returned from
placement new.  We tried some simplifications of that, but they failed
(details in the PR).  We tried simple fixes, like introducing a memory
barrier at placement new, but they caused optimization problems
(details in the PR).  The current patch appears to solve the problem
without introducing any significant optimization issues.

The approach used here is to define a new tree code,
CHANGE_DYNAMIC_TYPE_EXPR, which represents a change of the dynamic
type of a memory location.  The aliasing code recognizes this, and
marks the resulting pointer so as to not apply TBAA; this uses a new
flag in the tree_decl_common structure.  When alias analysis solves
the constraint graph, it propagates the TBAA marker as needed to
copies of the pointer (this code to do this is by Danny Berlin).

After alias analysis is done, we discard the CHANGE_DYNAMIC_TYPE_EXPR
nodes.  Keeping them leaves useless references to variables which
inhibit optimization.  But we need them until the alias analysis path
in order to take advantage of the contraint solver to propagate the
TBAA markers to other variables.

The rest of the patch is just bookkeeping for the new tree node and
the new decl flag.

Although this patch only handles C++ placement new, I believe that we
may be able to take advantage of it to loosen this restriction in
fold_builtin_memory_op.  However, I haven't tested this.

      /* With memcpy, it is possible to bypass aliasing rules, so without
         this check i. e. execute/20060930-2.c would be misoptimized, because
	 it use conflicting alias set to hold argument for the memcpy call.
	 This check is probably unnecesary with -fno-strict-aliasing. 
	 Similarly for destvar.  See also PR29286.  */
      if (!var_decl_component_p (srcvar)
	  /* Accept: memcpy (*char_var, "test", 1); that simplify
	     to char_var='t';  */
	  || is_gimple_min_invariant (srcvar)
	  || readonly_data_expr (src))
	return NULL_TREE;

This patch is mostly in the middle-end and thus under my purview, but
it requires approval for the nontrivial change to the C++ frontend.
Also, I would appreciate it if people could take a close look at it to
make sure there aren't any holes, particularly at the alias analysis
code which is the complicated part.

Bootstrapped and tested on i686-pc-linux-gnu.  Richard Guenther has
tested it on tramp3d.

Because of the complexity of the patch, and because the problem does
not affect much code, I plan to commit it only on mainline, and to
simply treat the bug as won't-fix for earlier releases.

Ian


gcc/ChangeLog:
2007-06-09  Ian Lance Taylor  <iant@google.com>
	    Daniel Berlin  <dberlin@dberlin.org>

	PR libstdc++/29286
	* tree.def: Add CHANGE_DYNAMIC_TYPE_EXPR.
	* tree-ssa-structalias.h (struct alias_info): Add
	no_tbaa_pruning_ptrs field.
	* tree-ssa-structalias.c (struct variable_info): Add
	no_tbaa_pruning field.
	(new_var_info): Initialize no_tbaa_pruning field.
	(unify_nodes): Copy no_tbaa_pruning field.
	(find_func_aliases): Handle CHANGE_DYNAMIC_TYPE_EXPR.
	(dump_solution_for_var): Print no_tbaa_pruning flag.
	(set_uids_in_ptset): Add no_tbaa_pruning parameter.  Change all
	callers.
	(compute_tbaa_pruning): New static function.
	(compute_points_to_sets): Remove CHANGE_DYNAMIC_TYPE_EXPR nodes.
	Call compute_tbaa_pruning.
	* tree-ssa-alias.c (init_alias_info): Initialize
	no_tbaa_pruning_ptrs field.
	(delete_alias_info): Destroy no_tbaa_pruning_ptrs field if
	necessary.
	(may_alias_p): Add ai parameter.  Change all callers.  Test
	no_tbaa_pruning_ptrs.
	* tree.h (CHANGE_DYNAMIC_TYPE_NEW_TYPE): Define.
	(CHANGE_DYNAMIC_TYPE_LOCATION): Define.
	(DECL_NO_TBAA_P): Define.
	(struct tree_decl_common): Add no_tbaa_flag field.
	* gimplify.c (gimplify_expr): Handle CHANGE_DYNAMIC_TYPE_EXPR.
	* gimple-low.c (lower_stmt): Likewise.
	* tree-gimple.c (is_gimple_stmt): Likewise.
	* tree-ssa-operands.c (get_expr_operands): Likewise.
	* tree-ssa-dce.c (mark_stmt_if_obviously_necessary): Likewise.
	* tree-inline.c (estimate_num_insns_1): Likewise.
	(copy_result_decl_to_var): Likewise.
	* expr.c (expand_expr_real_1): Likewise.
	* tree-pretty-print.c (dump_generic_node): Likewise.
	* tree-inline.c (copy_decl_to_var): Copy DECL_NO_TBAA_P flag.
	* omp-low.c (omp_copy_decl_2): Likewise.
	* print-tree.c (print_node): Print DECL_NO_TBAA_P flag.

gcc/cp/ChangeLog:
2007-06-09  Ian Lance Taylor  <iant@google.com>

	PR libstdc++/29286
	* init.c (avoid_placement_new_aliasing): New static function.
	(build_new_1): Call it.

gcc/testsuite/ChangeLog:
2007-06-09  Ian Lance Taylor  <iant@google.com>

	PR libstdc++/29286
	* g++.dg/init/new16.C: New test.
	* g++.dg/init/new17.C: New test.
	* g++.dg/init/new18.C: New test.
	* g++.dg/init/new19.C: New test.


Index: cp/init.c
===================================================================
--- cp/init.c	(revision 125592)
+++ cp/init.c	(working copy)
@@ -1564,6 +1564,55 @@ build_raw_new_expr (tree placement, tree
   return new_expr;
 }
 
+/* Make sure that there are no aliasing issues with T, a placement new
+   expression applied to PLACEMENT, by recording the change in dynamic
+   type.  If placement new is inlined, as it is with libstdc++, and if
+   the type of the placement new differs from the type of the
+   placement location itself, then alias analysis may think it is OK
+   to interchange writes to the location from before the placement new
+   and from after the placement new.  We have to prevent type-based
+   alias analysis from applying.  PLACEMENT may be NULL, which means
+   that we couldn't capture it in a temporary variable, in which case
+   we use a memory clobber.  */
+
+static tree
+avoid_placement_new_aliasing (tree t, tree placement)
+{
+  tree type_change;
+
+  if (processing_template_decl)
+    return t;
+
+  /* If we are not using type based aliasing, we don't have to do
+     anything.  */
+  if (!flag_strict_aliasing)
+    return t;
+
+  /* If we have a pointer and a location, record the change in dynamic
+     type.  Otherwise we need a general memory clobber.  */
+  if (TREE_CODE (TREE_TYPE (t)) == POINTER_TYPE
+      && placement != NULL_TREE
+      && TREE_CODE (TREE_TYPE (placement)) == POINTER_TYPE)
+    type_change = build_stmt (CHANGE_DYNAMIC_TYPE_EXPR,
+			      TREE_TYPE (t),
+			      placement);
+  else
+    {
+      /* Build a memory clobber.  */
+      type_change = build_stmt (ASM_EXPR,
+				build_string (0, ""),
+				NULL_TREE,
+				NULL_TREE,
+				tree_cons (NULL_TREE,
+					   build_string (6, "memory"),
+					   NULL_TREE));
+
+      ASM_VOLATILE_P (type_change) = 1;
+    }
+
+  return build2 (COMPOUND_EXPR, TREE_TYPE (t), type_change, t);
+}
+
 /* Generate code for a new-expression, including calling the "operator
    new" function, initializing the object, and, if an exception occurs
    during construction, cleaning up.  The arguments are as for
@@ -1607,6 +1656,7 @@ build_new_1 (tree placement, tree type, 
      beginning of the storage allocated for an array-new expression in
      order to store the number of elements.  */
   tree cookie_size = NULL_TREE;
+  tree placement_var;
   /* True if the function we are calling is a placement allocation
      function.  */
   bool placement_allocation_fn_p;
@@ -1700,6 +1750,20 @@ build_new_1 (tree placement, tree type, 
 
   alloc_fn = NULL_TREE;
 
+  /* If PLACEMENT is a simple pointer type, then copy it into
+     PLACEMENT_VAR.  */
+  if (processing_template_decl
+      || placement == NULL_TREE
+      || TREE_CHAIN (placement) != NULL_TREE
+      || TREE_CODE (TREE_TYPE (TREE_VALUE (placement))) != POINTER_TYPE)
+    placement_var = NULL_TREE;
+  else
+    {
+      placement_var = get_temp_regvar (TREE_TYPE (TREE_VALUE (placement)),
+				       TREE_VALUE (placement));
+      placement = tree_cons (NULL_TREE, placement_var, NULL_TREE);
+    }
+
   /* Allocate the object.  */
   if (! placement && TYPE_FOR_JAVA (elt_type))
     {
@@ -1792,7 +1856,12 @@ build_new_1 (tree placement, tree type, 
   /* In the simple case, we can stop now.  */
   pointer_type = build_pointer_type (type);
   if (!cookie_size && !is_initialized)
-    return build_nop (pointer_type, alloc_call);
+    {
+      rval = build_nop (pointer_type, alloc_call);
+      if (placement != NULL)
+	rval = avoid_placement_new_aliasing (rval, placement_var);
+      return rval;
+    }
 
   /* While we're working, use a pointer to the type we've actually
      allocated. Store the result of the call in a variable so that we
@@ -2052,6 +2121,9 @@ build_new_1 (tree placement, tree type, 
   /* A new-expression is never an lvalue.  */
   gcc_assert (!lvalue_p (rval));
 
+  if (placement != NULL)
+    rval = avoid_placement_new_aliasing (rval, placement_var);
+
   return rval;
 }
 
Index: tree-pretty-print.c
===================================================================
--- tree-pretty-print.c	(revision 125592)
+++ tree-pretty-print.c	(working copy)
@@ -1493,6 +1493,17 @@ dump_generic_node (pretty_printer *buffe
       is_expr = false;
       break;
 
+    case CHANGE_DYNAMIC_TYPE_EXPR:
+      pp_string (buffer, "<<<change_dynamic_type (");
+      dump_generic_node (buffer, CHANGE_DYNAMIC_TYPE_NEW_TYPE (node), spc + 2,
+			 flags, false);
+      pp_string (buffer, ") ");
+      dump_generic_node (buffer, CHANGE_DYNAMIC_TYPE_LOCATION (node), spc + 2,
+			 flags, false);
+      pp_string (buffer, ")>>>");
+      is_expr = false;
+      break;
+
     case LABEL_EXPR:
       op0 = TREE_OPERAND (node, 0);
       /* If this is for break or continue, don't bother printing it.  */
Index: tree.h
===================================================================
--- tree.h	(revision 125592)
+++ tree.h	(working copy)
@@ -1631,6 +1631,12 @@ struct tree_constructor GTY(())
 #define EH_FILTER_FAILURE(NODE)	TREE_OPERAND (EH_FILTER_EXPR_CHECK (NODE), 1)
 #define EH_FILTER_MUST_NOT_THROW(NODE) TREE_STATIC (EH_FILTER_EXPR_CHECK (NODE))
 
+/* CHANGE_DYNAMIC_TYPE_EXPR accessors.  */
+#define CHANGE_DYNAMIC_TYPE_NEW_TYPE(NODE) \
+  TREE_OPERAND (CHANGE_DYNAMIC_TYPE_EXPR_CHECK (NODE), 0)
+#define CHANGE_DYNAMIC_TYPE_LOCATION(NODE) \
+  TREE_OPERAND (CHANGE_DYNAMIC_TYPE_EXPR_CHECK (NODE), 1)
+
 /* OBJ_TYPE_REF accessors.  */
 #define OBJ_TYPE_REF_EXPR(NODE)	  TREE_OPERAND (OBJ_TYPE_REF_CHECK (NODE), 0)
 #define OBJ_TYPE_REF_OBJECT(NODE) TREE_OPERAND (OBJ_TYPE_REF_CHECK (NODE), 1)
@@ -2665,6 +2671,11 @@ struct tree_memory_partition_tag GTY(())
 #define DECL_GIMPLE_REG_P(DECL) \
   DECL_COMMON_CHECK (DECL)->decl_common.gimple_reg_flag
 
+/* For a DECL with pointer type, this is set if Type Based Alias
+   Analysis should not be applied to this DECL.  */
+#define DECL_NO_TBAA_P(DECL) \
+  DECL_COMMON_CHECK (DECL)->decl_common.no_tbaa_flag
+
 struct tree_decl_common GTY(())
 {
   struct tree_decl_minimal common;
@@ -2705,6 +2716,8 @@ struct tree_decl_common GTY(())
   /* Logically, these two would go in a theoretical base shared by var and
      parm decl. */
   unsigned gimple_reg_flag : 1;
+  /* In a DECL with pointer type, set if no TBAA should be done.  */
+  unsigned no_tbaa_flag : 1;
 
   union tree_decl_u1 {
     /* In a FUNCTION_DECL for which DECL_BUILT_IN holds, this is
Index: omp-low.c
===================================================================
--- omp-low.c	(revision 125592)
+++ omp-low.c	(working copy)
@@ -516,6 +516,7 @@ omp_copy_decl_2 (tree var, tree name, tr
 
   TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (var);
   DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (var);
+  DECL_NO_TBAA_P (copy) = DECL_NO_TBAA_P (var);
   DECL_ARTIFICIAL (copy) = DECL_ARTIFICIAL (var);
   DECL_IGNORED_P (copy) = DECL_IGNORED_P (var);
   TREE_USED (copy) = 1;
Index: tree-gimple.c
===================================================================
--- tree-gimple.c	(revision 125592)
+++ tree-gimple.c	(working copy)
@@ -222,6 +222,7 @@ is_gimple_stmt (tree t)
     case TRY_FINALLY_EXPR:
     case EH_FILTER_EXPR:
     case CATCH_EXPR:
+    case CHANGE_DYNAMIC_TYPE_EXPR:
     case ASM_EXPR:
     case RESX_EXPR:
     case PHI_NODE:
Index: tree-ssa-alias.c
===================================================================
--- tree-ssa-alias.c	(revision 125592)
+++ tree-ssa-alias.c	(working copy)
@@ -198,7 +198,8 @@ static bitmap_obstack alias_bitmap_obsta
 static void compute_flow_insensitive_aliasing (struct alias_info *);
 static void finalize_ref_all_pointers (struct alias_info *);
 static void dump_alias_stats (FILE *);
-static bool may_alias_p (tree, HOST_WIDE_INT, tree, HOST_WIDE_INT, bool);
+static bool may_alias_p (struct alias_info *, tree, HOST_WIDE_INT, tree,
+			 HOST_WIDE_INT, bool);
 static tree create_memory_tag (tree type, bool is_type_tag);
 static tree get_smt_for (tree, struct alias_info *);
 static tree get_nmt_for (tree);
@@ -1936,6 +1937,7 @@ init_alias_info (void)
   ai->written_vars = pointer_set_create ();
   ai->dereferenced_ptrs_store = pointer_set_create ();
   ai->dereferenced_ptrs_load = pointer_set_create ();
+  ai->no_tbaa_pruning_ptrs = NULL;
 
   /* Clear out all memory reference stats.  */
   init_mem_ref_stats ();
@@ -2048,6 +2050,8 @@ delete_alias_info (struct alias_info *ai
   pointer_set_destroy (ai->written_vars);
   pointer_set_destroy (ai->dereferenced_ptrs_store);
   pointer_set_destroy (ai->dereferenced_ptrs_load);
+  if (ai->no_tbaa_pruning_ptrs != NULL)
+    pointer_set_destroy (ai->no_tbaa_pruning_ptrs);
   free (ai);
 
   delete_points_to_sets ();
@@ -2311,7 +2315,7 @@ compute_flow_insensitive_aliasing (struc
 	  if (!tag_stored_p && !var_stored_p)
 	    continue;
 	     
-	  if (may_alias_p (p_map->var, p_map->set, var, v_map->set, false))
+	  if (may_alias_p (ai, p_map->var, p_map->set, var, v_map->set, false))
 	    {
 	      /* We should never have a var with subvars here, because
 	         they shouldn't get into the set of addressable vars */
@@ -2364,7 +2368,8 @@ compute_flow_insensitive_aliasing (struc
 	    continue;
 
 	  /* If the pointers may not point to each other, do nothing.  */
-	  if (!may_alias_p (p_map1->var, p_map1->set, tag2, p_map2->set, true))
+	  if (!may_alias_p (ai, p_map1->var, p_map1->set, tag2, p_map2->set,
+			    true))
 	    continue;
 
 	  /* The two pointers may alias each other.  If they already have
@@ -2671,7 +2676,8 @@ maybe_create_global_var (void)
    VAR_ALIAS_SET is the alias set for VAR.  */
 
 static bool
-may_alias_p (tree ptr, HOST_WIDE_INT mem_alias_set,
+may_alias_p (struct alias_info *ai,
+	     tree ptr, HOST_WIDE_INT mem_alias_set,
 	     tree var, HOST_WIDE_INT var_alias_set,
 	     bool alias_set_only)
 {
@@ -2720,69 +2726,73 @@ may_alias_p (tree ptr, HOST_WIDE_INT mem
 
   gcc_assert (TREE_CODE (mem) == SYMBOL_MEMORY_TAG);
 
-  alias_stats.tbaa_queries++;
-
-  /* If the alias sets don't conflict then MEM cannot alias VAR.  */
-  if (!alias_sets_conflict_p (mem_alias_set, var_alias_set))
+  if (ai->no_tbaa_pruning_ptrs == NULL
+      || !pointer_set_contains (ai->no_tbaa_pruning_ptrs, ptr))
     {
-      alias_stats.alias_noalias++;
-      alias_stats.tbaa_resolved++;
-      return false;
-    }
+      alias_stats.tbaa_queries++;
 
-  /* If VAR is a record or union type, PTR cannot point into VAR
-     unless there is some explicit address operation in the
-     program that can reference a field of the type pointed-to by PTR.
-     This also assumes that the types of both VAR and PTR are
-     contained within the compilation unit, and that there is no fancy
-     addressing arithmetic associated with any of the types
-     involved.  */
-  if (mem_alias_set != 0 && var_alias_set != 0)
-    {
-      tree ptr_type = TREE_TYPE (ptr);
-      tree var_type = TREE_TYPE (var);
-      
-      /* The star count is -1 if the type at the end of the pointer_to 
-	 chain is not a record or union type. */ 
-      if ((!alias_set_only) && 
-	  ipa_type_escape_star_count_of_interesting_type (var_type) >= 0)
+      /* If the alias sets don't conflict then MEM cannot alias VAR.  */
+      if (!alias_sets_conflict_p (mem_alias_set, var_alias_set))
 	{
-	  int ptr_star_count = 0;
-	  
-	  /* ipa_type_escape_star_count_of_interesting_type is a
-	     little too restrictive for the pointer type, need to
-	     allow pointers to primitive types as long as those types
-	     cannot be pointers to everything.  */
-	  while (POINTER_TYPE_P (ptr_type))
+	  alias_stats.alias_noalias++;
+	  alias_stats.tbaa_resolved++;
+	  return false;
+	}
+
+      /* If VAR is a record or union type, PTR cannot point into VAR
+	 unless there is some explicit address operation in the
+	 program that can reference a field of the type pointed-to by
+	 PTR.  This also assumes that the types of both VAR and PTR
+	 are contained within the compilation unit, and that there is
+	 no fancy addressing arithmetic associated with any of the
+	 types involved.  */
+      if (mem_alias_set != 0 && var_alias_set != 0)
+	{
+	  tree ptr_type = TREE_TYPE (ptr);
+	  tree var_type = TREE_TYPE (var);
+      
+	  /* The star count is -1 if the type at the end of the
+	     pointer_to chain is not a record or union type. */ 
+	  if ((!alias_set_only) && 
+	      ipa_type_escape_star_count_of_interesting_type (var_type) >= 0)
 	    {
-	      /* Strip the *s off.  */ 
-	      ptr_type = TREE_TYPE (ptr_type);
-	      ptr_star_count++;
-	    }
+	      int ptr_star_count = 0;
 	  
-	  /* There does not appear to be a better test to see if the 
-	     pointer type was one of the pointer to everything 
-	     types.  */
-	  if (ptr_star_count > 0)
-	    {
-	      alias_stats.structnoaddress_queries++;
-	      if (ipa_type_escape_field_does_not_clobber_p (var_type, 
-							    TREE_TYPE (ptr))) 
+	      /* ipa_type_escape_star_count_of_interesting_type is a
+		 little too restrictive for the pointer type, need to
+		 allow pointers to primitive types as long as those
+		 types cannot be pointers to everything.  */
+	      while (POINTER_TYPE_P (ptr_type))
+		{
+		  /* Strip the *s off.  */ 
+		  ptr_type = TREE_TYPE (ptr_type);
+		  ptr_star_count++;
+		}
+	  
+	      /* There does not appear to be a better test to see if
+		 the pointer type was one of the pointer to everything
+		 types.  */
+	      if (ptr_star_count > 0)
+		{
+		  alias_stats.structnoaddress_queries++;
+		  if (ipa_type_escape_field_does_not_clobber_p (var_type, 
+								TREE_TYPE (ptr)))
+		    {
+		      alias_stats.structnoaddress_resolved++;
+		      alias_stats.alias_noalias++;
+		      return false;
+		    }
+		}
+	      else if (ptr_star_count == 0)
 		{
+		  /* If PTR_TYPE was not really a pointer to type, it cannot 
+		     alias.  */ 
+		  alias_stats.structnoaddress_queries++;
 		  alias_stats.structnoaddress_resolved++;
 		  alias_stats.alias_noalias++;
 		  return false;
 		}
 	    }
-	  else if (ptr_star_count == 0)
-	    {
-	      /* If PTR_TYPE was not really a pointer to type, it cannot 
-		 alias.  */ 
-	      alias_stats.structnoaddress_queries++;
-	      alias_stats.structnoaddress_resolved++;
-	      alias_stats.alias_noalias++;
-	      return false;
-	    }
 	}
     }
 
Index: gimple-low.c
===================================================================
--- gimple-low.c	(revision 125592)
+++ gimple-low.c	(working copy)
@@ -242,6 +242,7 @@ lower_stmt (tree_stmt_iterator *tsi, str
     case GOTO_EXPR:
     case LABEL_EXPR:
     case SWITCH_EXPR:
+    case CHANGE_DYNAMIC_TYPE_EXPR:
     case OMP_FOR:
     case OMP_SECTIONS:
     case OMP_SECTION:
Index: expr.c
===================================================================
--- expr.c	(revision 125592)
+++ expr.c	(working copy)
@@ -8886,6 +8886,13 @@ expand_expr_real_1 (tree exp, rtx target
       /* Lowered by gimplify.c.  */
       gcc_unreachable ();
 
+    case CHANGE_DYNAMIC_TYPE_EXPR:
+      /* This is ignored at the RTL level.  The tree level set
+	 DECL_POINTER_ALIAS_SET of any variable to be 0, which is
+	 overkill for the RTL layer but is all that we can
+	 represent.  */
+      return const0_rtx;
+
     case EXC_PTR_EXPR:
       return get_exception_pointer (cfun);
 
Index: gimplify.c
===================================================================
--- gimplify.c	(revision 125592)
+++ gimplify.c	(working copy)
@@ -5791,6 +5791,11 @@ gimplify_expr (tree *expr_p, tree *pre_p
 	  ret = GS_ALL_DONE;
 	  break;
 
+	case CHANGE_DYNAMIC_TYPE_EXPR:
+	  ret = gimplify_expr (&CHANGE_DYNAMIC_TYPE_LOCATION (*expr_p),
+			       pre_p, post_p, is_gimple_reg, fb_lvalue);
+	  break;
+
 	case OBJ_TYPE_REF:
 	  {
 	    enum gimplify_status r0, r1;
Index: tree.def
===================================================================
--- tree.def	(revision 125592)
+++ tree.def	(working copy)
@@ -876,6 +876,15 @@ DEFTREECODE (CATCH_EXPR, "catch_expr", t
    expanding.  */
 DEFTREECODE (EH_FILTER_EXPR, "eh_filter_expr", tcc_statement, 2)
 
+/* Indicates a change in the dynamic type of a memory location.  This
+   has no value and generates no executable code.  It is only used for
+   type based alias analysis.  This is generated by C++ placement new.
+   CHANGE_DYNAMIC_TYPE_NEW_TYPE, the first operand, is the new type.
+   CHNAGE_DYNAMIC_TYPE_LOCATION, the second operand, is the location
+   whose type is being changed.  */
+DEFTREECODE (CHANGE_DYNAMIC_TYPE_EXPR, "change_dynamic_type_expr",
+	     tcc_statement, 2)
+
 /* Node used for describing a property that is known at compile
    time.  */
 DEFTREECODE (SCEV_KNOWN, "scev_known", tcc_expression, 0)
Index: print-tree.c
===================================================================
--- print-tree.c	(revision 125592)
+++ print-tree.c	(working copy)
@@ -401,7 +401,9 @@ print_node (FILE *file, const char *pref
 	  if (DECL_VIRTUAL_P (node))
 	    fputs (" virtual", file);
 	  if (DECL_PRESERVE_P (node))
-	    fputs (" preserve", file);	  
+	    fputs (" preserve", file);
+	  if (DECL_NO_TBAA_P (node))
+	    fputs (" no-tbaa", file);
 	  if (DECL_LANG_FLAG_0 (node))
 	    fputs (" decl_0", file);
 	  if (DECL_LANG_FLAG_1 (node))
Index: tree-ssa-dce.c
===================================================================
--- tree-ssa-dce.c	(revision 125592)
+++ tree-ssa-dce.c	(working copy)
@@ -286,6 +286,7 @@ mark_stmt_if_obviously_necessary (tree s
     case ASM_EXPR:
     case RESX_EXPR:
     case RETURN_EXPR:
+    case CHANGE_DYNAMIC_TYPE_EXPR:
       mark_stmt_necessary (stmt, true);
       return;
 
Index: tree-inline.c
===================================================================
--- tree-inline.c	(revision 125592)
+++ tree-inline.c	(working copy)
@@ -2030,6 +2030,11 @@ estimate_num_insns_1 (tree *tp, int *wal
       *walk_subtrees = 0;
       return NULL;
 
+      /* CHANGE_DYNAMIC_TYPE_EXPR explicitly expands to nothing.  */
+    case CHANGE_DYNAMIC_TYPE_EXPR:
+      *walk_subtrees = 0;
+      return NULL;
+
     /* Try to estimate the cost of assignments.  We have three cases to
        deal with:
 	1) Simple assignments to registers;
@@ -3217,6 +3222,7 @@ copy_decl_to_var (tree decl, copy_body_d
   TREE_READONLY (copy) = TREE_READONLY (decl);
   TREE_THIS_VOLATILE (copy) = TREE_THIS_VOLATILE (decl);
   DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (decl);
+  DECL_NO_TBAA_P (copy) = DECL_NO_TBAA_P (decl);
 
   return copy_decl_for_dup_finish (id, decl, copy);
 }
@@ -3243,6 +3249,7 @@ copy_result_decl_to_var (tree decl, copy
     {
       TREE_ADDRESSABLE (copy) = TREE_ADDRESSABLE (decl);
       DECL_GIMPLE_REG_P (copy) = DECL_GIMPLE_REG_P (decl);
+      DECL_NO_TBAA_P (copy) = DECL_NO_TBAA_P (decl);
     }
 
   return copy_decl_for_dup_finish (id, decl, copy);
Index: tree-ssa-structalias.c
===================================================================
--- tree-ssa-structalias.c	(revision 125592)
+++ tree-ssa-structalias.c	(working copy)
@@ -250,6 +250,10 @@ struct variable_info
   /* True if this is a heap variable.  */
   unsigned int is_heap_var:1;
 
+  /* True if we may not use TBAA to prune references to this
+     variable.  This is used for C++ placement new.  */
+  unsigned int no_tbaa_pruning : 1;
+
   /* Points-to set for this variable.  */
   bitmap solution;
 
@@ -359,6 +363,7 @@ static varinfo_t
 new_var_info (tree t, unsigned int id, const char *name)
 {
   varinfo_t ret = (varinfo_t) pool_alloc (variable_info_pool);
+  tree var;
 
   ret->id = id;
   ret->name = name;
@@ -369,6 +374,12 @@ new_var_info (tree t, unsigned int id, c
   ret->is_special_var = false;
   ret->is_unknown_size_var = false;
   ret->has_union = false;
+  var = t;
+  if (TREE_CODE (var) == SSA_NAME)
+    var = SSA_NAME_VAR (var);
+  ret->no_tbaa_pruning = (DECL_P (var)
+			  && POINTER_TYPE_P (TREE_TYPE (var))
+			  && DECL_NO_TBAA_P (var));
   ret->solution = BITMAP_ALLOC (&pta_obstack);
   ret->oldsolution = BITMAP_ALLOC (&oldpta_obstack);
   ret->next = NULL;
@@ -1195,6 +1206,9 @@ unify_nodes (constraint_graph_t graph, u
   merge_graph_nodes (graph, to, from);
   merge_node_constraints (graph, to, from);
 
+  if (get_varinfo (from)->no_tbaa_pruning)
+    get_varinfo (to)->no_tbaa_pruning = true;
+
   if (update_changed && TEST_BIT (changed, from))
     {
       RESET_BIT (changed, from);
@@ -3563,6 +3577,14 @@ find_func_aliases (tree origt)
 	    }
 	}
     }
+  else if (TREE_CODE (t) == CHANGE_DYNAMIC_TYPE_EXPR)
+    {
+      unsigned int j;
+
+      get_constraint_for (CHANGE_DYNAMIC_TYPE_LOCATION (t), &lhsc);
+      for (j = 0; VEC_iterate (ce_s, lhsc, j, c); ++j)
+	get_varinfo (c->var)->no_tbaa_pruning = true;
+    }
 
   /* After promoting variables and computing aliasing we will
      need to re-scan most statements.  FIXME: Try to minimize the
@@ -4130,7 +4152,10 @@ dump_solution_for_var (FILE *file, unsig
 	{
 	  fprintf (file, "%s ", get_varinfo (i)->name);
 	}
-      fprintf (file, "}\n");
+      fprintf (file, "}");
+      if (vi->no_tbaa_pruning)
+	fprintf (file, " no-tbaa-pruning");
+      fprintf (file, "\n");
     }
 }
 
@@ -4290,10 +4315,12 @@ shared_bitmap_add (bitmap pt_vars)
 /* Set bits in INTO corresponding to the variable uids in solution set
    FROM, which came from variable PTR.
    For variables that are actually dereferenced, we also use type
-   based alias analysis to prune the points-to sets.  */
+   based alias analysis to prune the points-to sets.
+   If NO_TBAA_PRUNING is true, we do not perform TBAA pruning of the
+   from set.  */
 
 static void
-set_uids_in_ptset (tree ptr, bitmap into, bitmap from)
+set_uids_in_ptset (tree ptr, bitmap into, bitmap from, bool no_tbaa_pruning)
 {
   unsigned int i;
   bitmap_iterator bi;
@@ -4329,7 +4356,7 @@ set_uids_in_ptset (tree ptr, bitmap into
 	      if (sft)
 		{
 		  var_alias_set = get_alias_set (sft);
-		  if (!vi->directly_dereferenced
+		  if (no_tbaa_pruning
 		      || alias_sets_conflict_p (ptr_alias_set, var_alias_set))
 		    bitmap_set_bit (into, DECL_UID (sft));
 		}
@@ -4343,7 +4370,7 @@ set_uids_in_ptset (tree ptr, bitmap into
 	      else
 		{
 		  var_alias_set = get_alias_set (vi->decl);
-		  if (!vi->directly_dereferenced
+		  if (no_tbaa_pruning
 		      || alias_sets_conflict_p (ptr_alias_set, var_alias_set))
 		    bitmap_set_bit (into, DECL_UID (vi->decl));
 		}
@@ -4546,7 +4573,9 @@ find_what_p_points_to (tree p)
 	      pi->pt_global_mem = 1;
 	    }
 	  
-	  set_uids_in_ptset (vi->decl, finished_solution, vi->solution);
+	  set_uids_in_ptset (vi->decl, finished_solution, vi->solution,
+			     (vi->no_tbaa_pruning
+			      || !vi->directly_dereferenced));
 	  result = shared_bitmap_lookup (finished_solution);
 
 	  if (!result)
@@ -4772,6 +4801,147 @@ remove_preds_and_fake_succs (constraint_
   bitmap_obstack_release (&predbitmap_obstack);
 }
 
+/* Compute the set of variables we can't TBAA prune.  */
+
+static void
+compute_tbaa_pruning (struct alias_info *ai)
+{
+  unsigned int size = VEC_length (varinfo_t, varmap);
+  unsigned int i;
+  bool any;
+
+  changed_count = 0;
+  changed = sbitmap_alloc (size);
+  sbitmap_zero (changed);
+
+  /* Mark all initial no_tbaa_pruning nodes as changed.  */
+  any = false;
+  for (i = 0; i < size; ++i)
+    {
+      varinfo_t ivi = get_varinfo (i);
+
+      if (find (i) == i && ivi->no_tbaa_pruning)
+	{
+	  any = true;
+	  if ((graph->succs[i] && !bitmap_empty_p (graph->succs[i]))
+	      || VEC_length (constraint_t, graph->complex[i]) > 0)
+	    {
+	      SET_BIT (changed, i);
+	      ++changed_count;
+	    }
+	}
+    }
+
+  while (changed_count > 0)
+    {
+      struct topo_info *ti = init_topo_info ();
+      ++stats.iterations;
+
+      bitmap_obstack_initialize (&iteration_obstack);
+
+      compute_topo_order (graph, ti);
+
+      while (VEC_length (unsigned, ti->topo_order) != 0)
+	{
+	  bitmap_iterator bi;
+
+	  i = VEC_pop (unsigned, ti->topo_order);
+
+	  /* If this variable is not a representative, skip it.  */
+	  if (find (i) != i)
+	    continue;
+
+	  /* If the node has changed, we need to process the complex
+	     constraints and outgoing edges again.  */
+	  if (TEST_BIT (changed, i))
+	    {
+	      unsigned int j;
+	      constraint_t c;
+	      VEC(constraint_t,heap) *complex = graph->complex[i];
+
+	      RESET_BIT (changed, i);
+	      --changed_count;
+
+	      /* Process the complex copy constraints.  */
+	      for (j = 0; VEC_iterate (constraint_t, complex, j, c); ++j)
+		{
+		  if (c->lhs.type == SCALAR && c->rhs.type == SCALAR)
+		    {
+		      varinfo_t lhsvi = get_varinfo (find (c->lhs.var));
+
+		      if (!lhsvi->no_tbaa_pruning)
+			{
+			  lhsvi->no_tbaa_pruning = true;
+			  if (!TEST_BIT (changed, lhsvi->id))
+			    {
+			      SET_BIT (changed, lhsvi->id);
+			      ++changed_count;
+			    }
+			}
+		    }
+		}
+
+	      /* Propagate to all successors.  */
+	      EXECUTE_IF_IN_NONNULL_BITMAP (graph->succs[i], 0, j, bi)
+		{
+		  unsigned int to = find (j);
+		  varinfo_t tovi = get_varinfo (to);
+
+		  /* Don't propagate to ourselves.  */
+		  if (to == i)
+		    continue;
+
+		  if (!tovi->no_tbaa_pruning)
+		    {
+		      tovi->no_tbaa_pruning = true;
+		      if (!TEST_BIT (changed, to))
+			{
+			  SET_BIT (changed, to);
+			  ++changed_count;
+			}
+		    }
+		}
+	    }
+	}
+
+      free_topo_info (ti);
+      bitmap_obstack_release (&iteration_obstack);
+    }
+
+  sbitmap_free (changed);
+
+  if (any)
+    {
+      if (ai->no_tbaa_pruning_ptrs == NULL)
+	ai->no_tbaa_pruning_ptrs = pointer_set_create ();
+
+      for (i = 0; i < size; ++i)
+	{
+	  varinfo_t ivi = get_varinfo (i);
+	  varinfo_t ivip = get_varinfo (find (i));
+
+	  if (ivip->no_tbaa_pruning)
+	    {
+	      tree var = ivi->decl;
+
+	      if (TREE_CODE (var) == SSA_NAME)
+		var = SSA_NAME_VAR (var);
+
+	      if (POINTER_TYPE_P (TREE_TYPE (var)))
+		{
+		  DECL_NO_TBAA_P (var) = 1;
+
+		  pointer_set_insert (ai->no_tbaa_pruning_ptrs, var);
+
+		  /* Tell the RTL layer that this pointer can alias
+		     anything.  */
+		  DECL_POINTER_ALIAS_SET (var) = 0;
+		}
+	    }
+	}
+    }
+}
+
 /* Create points-to sets for the current function.  See the comments
    at the start of the file for an algorithmic overview.  */
 
@@ -4808,7 +4978,7 @@ compute_points_to_sets (struct alias_inf
 	    }
 	}
 
-      for (bsi = bsi_start (bb); !bsi_end_p (bsi); bsi_next (&bsi))
+      for (bsi = bsi_start (bb); !bsi_end_p (bsi); )
 	{
 	  tree stmt = bsi_stmt (bsi);
 
@@ -4819,6 +4989,13 @@ compute_points_to_sets (struct alias_inf
 	     This is used when creating name tags and alias
 	     sets.  */
 	  update_alias_info (stmt, ai);
+
+	  /* The information in CHANGE_DYNAMIC_TYPE_EXPR nodes has now
+	     been captured, and we can remove them.  */
+	  if (TREE_CODE (stmt) == CHANGE_DYNAMIC_TYPE_EXPR)
+	    bsi_remove (&bsi, true);
+	  else
+	    bsi_next (&bsi);
 	}
     }
 
@@ -4850,6 +5027,8 @@ compute_points_to_sets (struct alias_inf
 
   solve_graph (graph);
 
+  compute_tbaa_pruning (ai);
+
   if (dump_file)
     dump_sa_points_to_info (dump_file);
 
Index: tree-ssa-structalias.h
===================================================================
--- tree-ssa-structalias.h	(revision 125592)
+++ tree-ssa-structalias.h	(working copy)
@@ -59,6 +59,10 @@ struct alias_info
   /* Pointers that have been used in an indirect load operation.  */
   struct pointer_set_t *dereferenced_ptrs_load;
 
+  /* Pointers that are not subject to TBAA pruning.  This can be NULL
+     if there are none.  */
+  struct pointer_set_t *no_tbaa_pruning_ptrs;
+
   /* Memory tag for all the PTR_IS_REF_ALL pointers.  */
   tree ref_all_symbol_mem_tag;
 };
Index: tree-ssa-operands.c
===================================================================
--- tree-ssa-operands.c	(revision 125592)
+++ tree-ssa-operands.c	(working copy)
@@ -2266,6 +2266,10 @@ get_expr_operands (tree stmt, tree *expr
         return;
       }
 
+    case CHANGE_DYNAMIC_TYPE_EXPR:
+      get_expr_operands (stmt, &CHANGE_DYNAMIC_TYPE_LOCATION (expr), opf_use);
+      return;
+
     case BLOCK:
     case FUNCTION_DECL:
     case EXC_PTR_EXPR:
Index: testsuite/g++.dg/init/new17.C
===================================================================
--- testsuite/g++.dg/init/new17.C	(revision 0)
+++ testsuite/g++.dg/init/new17.C	(revision 0)
@@ -0,0 +1,37 @@
+// { dg-do compile }
+// { dg-options "-O2 -fstrict-aliasing -fdump-tree-final_cleanup" }
+
+// Test that placement new does not introduce an unnecessary memory
+// barrier.
+// See PR 29286.
+
+typedef __SIZE_TYPE__ size_t;
+
+inline void* operator new(size_t, void* __p) throw() { return __p; }
+
+template <class T, int D>
+class Vector
+{
+public:
+   Vector()
+   {
+     for (int i = 0; i < D; ++i)
+        new (&x_m[i]) T();
+   }
+   T& operator[](int i) { return x_m[i]; }
+
+private:
+   T x_m[D];
+};
+
+void foo(Vector<float, 3> *m)
+{
+  Vector<float, 3> v;
+  v[0] = 1.0;
+  v[1] = 2.0;
+  v[3] = 3.0;
+  *m = v;
+}
+
+// { dg-final { scan-tree-dump-times "= 0\.0" 1 "final_cleanup" } }
+// { dg-final { cleanup-tree-dump "final_cleanup" } }
Index: testsuite/g++.dg/init/new18.C
===================================================================
--- testsuite/g++.dg/init/new18.C	(revision 0)
+++ testsuite/g++.dg/init/new18.C	(revision 0)
@@ -0,0 +1,45 @@
+// { dg-do compile }
+// { dg-options "-O2 -fstrict-aliasing" }
+
+// This caused an ICE during placement new.
+
+namespace Pooma {
+   typedef int Context_t;
+   namespace Arch {
+   }
+   inline Context_t context() {
+  }
+   inline int contexts() {
+  }
+ }
+template<class DomT, class T, class NewDom1T> struct DomainTraitsScalar {
+  };
+template<class T> struct DomainTraits : public DomainTraitsScalar<T, T, T> {
+  };
+template<int Dim> class Grid;
+template<class DT> class DomainBase {
+  };
+template<int Dim, class DT> class Domain : public DomainBase<DT> {
+  };
+#include <vector>
+template<> class Grid<1> : public Domain<1, DomainTraits<Grid<1> > > {
+  };
+namespace Pooma {
+ class PatchSizeSyncer {
+    typedef Grid<1> Grid_t;
+    PatchSizeSyncer(int contextKey, Grid_t &localGrid);
+    int myContext_m;
+    int numContexts_m;
+    int localKey_m;
+    Grid_t localGrid_m;
+    typedef std::pair<int,Grid_t *> Elem_t;
+    std::vector<Elem_t> gridList_m;
+  };
+ }
+namespace Pooma {
+ PatchSizeSyncer::PatchSizeSyncer(int contextKey, Grid_t &localGrid)   :
+myContext_m(Pooma::context()),     numContexts_m(Pooma::contexts()),    
+localKey_m(contextKey),     localGrid_m(localGrid) {
+    if (myContext_m == 0) gridList_m.reserve(numContexts_m);
+  }
+ }
Index: testsuite/g++.dg/init/new19.C
===================================================================
--- testsuite/g++.dg/init/new19.C	(revision 0)
+++ testsuite/g++.dg/init/new19.C	(revision 0)
@@ -0,0 +1,73 @@
+// { dg-do compile }
+// { dg-options "-O2 -fstrict-aliasing -fdump-tree-lim-details" }
+
+// Make sure we hoist invariants out of the loop even in the presence
+// of placement new.  This is similar to code in tramp3d.
+
+typedef __SIZE_TYPE__ size_t;
+
+inline void* operator new(size_t, void* __p) throw() { return __p; }
+
+template <class T, int D>
+class Vector
+{
+public:
+   Vector()
+   {
+     for (int i = 0; i < D; ++i)
+        new (&x_m[i]) T();
+   }
+   T& operator[](int i) { return x_m[i]; }
+
+private:
+   T x_m[D];
+};
+
+struct sia
+{
+  int ai[3];
+};
+
+struct s
+{
+  struct si
+  {
+    sia* p;
+  } asi[3];
+  float* pd;
+};
+
+class c
+{
+  int foo(int, int, int);
+  s sm;
+};
+
+
+extern void bar(Vector<float, 3>*, int);
+int c::foo(int f1, int f2, int f3)
+{
+  float sum = 0;
+  for (int i = 0; i < 3; ++i)
+    {
+      for (int j = 0; j < 3; ++j)
+	{
+	  Vector<float, 3> v;
+	  v[0] = 1.0;
+	  v[1] = 2.0;
+	  v[2] = 3.0;
+	  for (int k = 0; k < 3; ++k)
+	    {
+	      float f = (f1 * this->sm.asi[0].p->ai[0]
+			 + f2 * this->sm.asi[1].p->ai[0]
+			 + f3 * this->sm.asi[2].p->ai[0]);
+	      sum += f * v[k];
+	    }
+	  *this->sm.pd = sum;
+	}
+    }
+  return sum;
+}
+
+// { dg-final { scan-tree-dump "Moving statement\\n.*->ai\\\[0\\\];\\n.*out of loop" "lim" } }
+// { dg-final { cleanup-tree-dump "lim" } }
Index: testsuite/g++.dg/init/new16.C
===================================================================
--- testsuite/g++.dg/init/new16.C	(revision 0)
+++ testsuite/g++.dg/init/new16.C	(revision 0)
@@ -0,0 +1,38 @@
+// { dg-do run }
+// { dg-options "-O2 -fstrict-aliasing" }
+
+// Test that we don't let TBAA reorder an assignment across a
+// placement new.
+// See PR 29286.
+
+typedef __SIZE_TYPE__ size_t;
+
+inline void* operator new(size_t, void* __p) throw() { return __p; }
+
+void __attribute__((noinline)) bar() {}
+
+long __attribute__((noinline)) foo(double *p, int n)
+{
+  long *f;
+  for (int i=0; i<n; ++i)
+  {
+    int *l = (int *)p;
+    *l = 0;
+    f = new (p) long;
+    *f = -1;
+  }
+  bar ();
+  return *f;
+}
+
+extern "C" void abort(void);
+int main()
+{
+  union {
+    int i;
+    long l;
+  } u;
+  if (foo((double *)&u, 1) != -1)
+    abort ();
+  return 0;
+}


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]