diff gcc/tree-ssa-dse.c @ 132:d34655255c78

update gcc-8.2
author mir3636
date Thu, 25 Oct 2018 10:21:07 +0900
parents 84e7813d76e9
children 1830386684a0
line wrap: on
line diff
--- a/gcc/tree-ssa-dse.c	Thu Oct 25 08:08:40 2018 +0900
+++ b/gcc/tree-ssa-dse.c	Thu Oct 25 10:21:07 2018 +0900
@@ -1,5 +1,5 @@
 /* Dead store elimination
-   Copyright (C) 2004-2017 Free Software Foundation, Inc.
+   Copyright (C) 2004-2018 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -35,6 +35,7 @@
 #include "tree-cfgcleanup.h"
 #include "params.h"
 #include "alias.h"
+#include "tree-ssa-loop.h"
 
 /* This file implements dead store elimination.
 
@@ -128,36 +129,48 @@
 valid_ao_ref_for_dse (ao_ref *ref)
 {
   return (ao_ref_base (ref)
-	  && ref->max_size != -1
-	  && ref->size != 0
-	  && ref->max_size == ref->size
-	  && ref->offset >= 0
-	  && (ref->offset % BITS_PER_UNIT) == 0
-	  && (ref->size % BITS_PER_UNIT) == 0
-	  && (ref->size != -1));
+	  && known_size_p (ref->max_size)
+	  && maybe_ne (ref->size, 0)
+	  && known_eq (ref->max_size, ref->size)
+	  && known_ge (ref->offset, 0)
+	  && multiple_p (ref->offset, BITS_PER_UNIT)
+	  && multiple_p (ref->size, BITS_PER_UNIT));
 }
 
-/* Normalize COPY (an ao_ref) relative to REF.  Essentially when we are
-   done COPY will only refer bytes found within REF.
+/* Try to normalize COPY (an ao_ref) relative to REF.  Essentially when we are
+   done COPY will only refer bytes found within REF.  Return true if COPY
+   is known to intersect at least one byte of REF.  */
 
-   We have already verified that COPY intersects at least one
-   byte with REF.  */
-
-static void
+static bool
 normalize_ref (ao_ref *copy, ao_ref *ref)
 {
+  if (!ordered_p (copy->offset, ref->offset))
+    return false;
+
   /* If COPY starts before REF, then reset the beginning of
      COPY to match REF and decrease the size of COPY by the
      number of bytes removed from COPY.  */
-  if (copy->offset < ref->offset)
+  if (maybe_lt (copy->offset, ref->offset))
     {
-      copy->size -= (ref->offset - copy->offset);
+      poly_int64 diff = ref->offset - copy->offset;
+      if (maybe_le (copy->size, diff))
+	return false;
+      copy->size -= diff;
       copy->offset = ref->offset;
     }
 
+  poly_int64 diff = copy->offset - ref->offset;
+  if (maybe_le (ref->size, diff))
+    return false;
+
   /* If COPY extends beyond REF, chop off its size appropriately.  */
-  if (copy->offset + copy->size > ref->offset + ref->size)
-    copy->size -= (copy->offset + copy->size - (ref->offset + ref->size));
+  poly_int64 limit = ref->size - diff;
+  if (!ordered_p (limit, copy->size))
+    return false;
+
+  if (maybe_gt (copy->size, limit))
+    copy->size = limit;
+  return true;
 }
 
 /* Clear any bytes written by STMT from the bitmap LIVE_BYTES.  The base
@@ -176,19 +189,15 @@
 
   /* Verify we have the same base memory address, the write
      has a known size and overlaps with REF.  */
+  HOST_WIDE_INT start, size;
   if (valid_ao_ref_for_dse (&write)
       && operand_equal_p (write.base, ref->base, OEP_ADDRESS_OF)
-      && write.size == write.max_size
-      && ((write.offset < ref->offset
-	   && write.offset + write.size > ref->offset)
-	  || (write.offset >= ref->offset
-	      && write.offset < ref->offset + ref->size)))
-    {
-      normalize_ref (&write, ref);
-      bitmap_clear_range (live_bytes,
-			  (write.offset - ref->offset) / BITS_PER_UNIT,
-			  write.size / BITS_PER_UNIT);
-    }
+      && known_eq (write.size, write.max_size)
+      && normalize_ref (&write, ref)
+      && (write.offset - ref->offset).is_constant (&start)
+      && write.size.is_constant (&size))
+    bitmap_clear_range (live_bytes, start / BITS_PER_UNIT,
+			size / BITS_PER_UNIT);
 }
 
 /* REF is a memory write.  Extract relevant information from it and
@@ -198,12 +207,14 @@
 static bool
 setup_live_bytes_from_ref (ao_ref *ref, sbitmap live_bytes)
 {
+  HOST_WIDE_INT const_size;
   if (valid_ao_ref_for_dse (ref)
-      && (ref->size / BITS_PER_UNIT
+      && ref->size.is_constant (&const_size)
+      && (const_size / BITS_PER_UNIT
 	  <= PARAM_VALUE (PARAM_DSE_MAX_OBJECT_SIZE)))
     {
       bitmap_clear (live_bytes);
-      bitmap_set_range (live_bytes, 0, ref->size / BITS_PER_UNIT);
+      bitmap_set_range (live_bytes, 0, const_size / BITS_PER_UNIT);
       return true;
     }
   return false;
@@ -228,14 +239,40 @@
      the REF to compute the trims.  */
 
   /* Now identify how much, if any of the tail we can chop off.  */
-  int last_orig = (ref->size / BITS_PER_UNIT) - 1;
+  HOST_WIDE_INT const_size;
   int last_live = bitmap_last_set_bit (live);
-  *trim_tail = (last_orig - last_live) & ~0x1;
+  if (ref->size.is_constant (&const_size))
+    {
+      int last_orig = (const_size / BITS_PER_UNIT) - 1;
+      /* We can leave inconvenient amounts on the tail as
+	 residual handling in mem* and str* functions is usually
+	 reasonably efficient.  */
+      *trim_tail = last_orig - last_live;
+
+      /* But don't trim away out of bounds accesses, as this defeats
+	 proper warnings.
+
+	 We could have a type with no TYPE_SIZE_UNIT or we could have a VLA
+	 where TYPE_SIZE_UNIT is not a constant.  */
+      if (*trim_tail
+	  && TYPE_SIZE_UNIT (TREE_TYPE (ref->base))
+	  && TREE_CODE (TYPE_SIZE_UNIT (TREE_TYPE (ref->base))) == INTEGER_CST
+	  && compare_tree_int (TYPE_SIZE_UNIT (TREE_TYPE (ref->base)),
+			       last_orig) <= 0)
+	*trim_tail = 0;
+    }
+  else
+    *trim_tail = 0;
 
   /* Identify how much, if any of the head we can chop off.  */
   int first_orig = 0;
   int first_live = bitmap_first_set_bit (live);
-  *trim_head = (first_live - first_orig) & ~0x1;
+  *trim_head = first_live - first_orig;
+
+  /* If more than a word remains, then make sure to keep the
+     starting point at least word aligned.  */
+  if (last_live - first_live > UNITS_PER_WORD)
+    *trim_head &= ~(UNITS_PER_WORD - 1);
 
   if ((*trim_head || *trim_tail)
       && dump_file && (dump_flags & TDF_DETAILS))
@@ -264,7 +301,7 @@
      least half the size of the object to ensure we're trimming
      the entire real or imaginary half.  By writing things this
      way we avoid more O(n) bitmap operations.  */
-  if (trim_tail * 2 >= ref->size / BITS_PER_UNIT)
+  if (known_ge (trim_tail * 2 * BITS_PER_UNIT, ref->size))
     {
       /* TREE_REALPART is live */
       tree x = TREE_REALPART (gimple_assign_rhs1 (stmt));
@@ -273,7 +310,7 @@
       gimple_assign_set_lhs (stmt, y);
       gimple_assign_set_rhs1 (stmt, x);
     }
-  else if (trim_head * 2 >= ref->size / BITS_PER_UNIT)
+  else if (known_ge (trim_head * 2 * BITS_PER_UNIT, ref->size))
     {
       /* TREE_IMAGPART is live */
       tree x = TREE_IMAGPART (gimple_assign_rhs1 (stmt));
@@ -323,7 +360,8 @@
 	return;
 
       /* The number of bytes for the new constructor.  */
-      int count = (ref->size / BITS_PER_UNIT) - head_trim - tail_trim;
+      poly_int64 ref_bytes = exact_div (ref->size, BITS_PER_UNIT);
+      poly_int64 count = ref_bytes - head_trim - tail_trim;
 
       /* And the new type for the CONSTRUCTOR.  Essentially it's just
 	 a char array large enough to cover the non-trimmed parts of
@@ -480,38 +518,56 @@
 {
   /* We have already verified that USE_REF and REF hit the same object.
      Now verify that there's actually an overlap between USE_REF and REF.  */
-  if (ranges_overlap_p (use_ref.offset, use_ref.size, ref->offset, ref->size))
+  HOST_WIDE_INT start, size;
+  if (normalize_ref (&use_ref, ref)
+      && (use_ref.offset - ref->offset).is_constant (&start)
+      && use_ref.size.is_constant (&size))
     {
-      normalize_ref (&use_ref, ref);
-
       /* If USE_REF covers all of REF, then it will hit one or more
 	 live bytes.   This avoids useless iteration over the bitmap
 	 below.  */
-      if (use_ref.offset <= ref->offset
-	  && use_ref.offset + use_ref.size >= ref->offset + ref->size)
+      if (start == 0 && known_eq (size, ref->size))
 	return true;
 
       /* Now check if any of the remaining bits in use_ref are set in LIVE.  */
-      unsigned int start = (use_ref.offset - ref->offset) / BITS_PER_UNIT;
-      unsigned int end = start + (use_ref.size / BITS_PER_UNIT) - 1;
-      return bitmap_bit_in_range_p (live, start, end);
+      return bitmap_bit_in_range_p (live, start / BITS_PER_UNIT,
+				    (start + size - 1) / BITS_PER_UNIT);
     }
   return true;
 }
 
+/* Callback for dse_classify_store calling for_each_index.  Verify that
+   indices are invariant in the loop with backedge PHI in basic-block DATA.  */
+
+static bool
+check_name (tree, tree *idx, void *data)
+{
+  basic_block phi_bb = (basic_block) data;
+  if (TREE_CODE (*idx) == SSA_NAME
+      && !SSA_NAME_IS_DEFAULT_DEF (*idx)
+      && dominated_by_p (CDI_DOMINATORS, gimple_bb (SSA_NAME_DEF_STMT (*idx)),
+			 phi_bb))
+    return false;
+  return true;
+}
+
 /* A helper of dse_optimize_stmt.
-   Given a GIMPLE_ASSIGN in STMT that writes to REF, find a candidate
-   statement *USE_STMT that may prove STMT to be dead.
-   Return TRUE if the above conditions are met, otherwise FALSE.  */
+   Given a GIMPLE_ASSIGN in STMT that writes to REF, classify it
+   according to downstream uses and defs.  Sets *BY_CLOBBER_P to true
+   if only clobber statements influenced the classification result.
+   Returns the classification.  */
 
 static dse_store_status
-dse_classify_store (ao_ref *ref, gimple *stmt, gimple **use_stmt,
-		    bool byte_tracking_enabled, sbitmap live_bytes)
+dse_classify_store (ao_ref *ref, gimple *stmt,
+		    bool byte_tracking_enabled, sbitmap live_bytes,
+		    bool *by_clobber_p = NULL)
 {
   gimple *temp;
-  unsigned cnt = 0;
+  int cnt = 0;
+  auto_bitmap visited;
 
-  *use_stmt = NULL;
+  if (by_clobber_p)
+    *by_clobber_p = true;
 
   /* Find the first dominated statement that clobbers (part of) the
      memory stmt stores to with no intermediate statement that may use
@@ -520,60 +576,55 @@
   temp = stmt;
   do
     {
-      gimple *use_stmt, *defvar_def;
+      gimple *use_stmt;
       imm_use_iterator ui;
       bool fail = false;
       tree defvar;
 
-      /* Limit stmt walking to be linear in the number of possibly
-         dead stores.  */
-      if (++cnt > 256)
-	return DSE_STORE_LIVE;
-
       if (gimple_code (temp) == GIMPLE_PHI)
-	defvar = PHI_RESULT (temp);
+	{
+	  /* If we visit this PHI by following a backedge then we have to
+	     make sure ref->ref only refers to SSA names that are invariant
+	     with respect to the loop represented by this PHI node.  */
+	  if (dominated_by_p (CDI_DOMINATORS, gimple_bb (stmt),
+			      gimple_bb (temp))
+	      && !for_each_index (ref->ref ? &ref->ref : &ref->base,
+				  check_name, gimple_bb (temp)))
+	    return DSE_STORE_LIVE;
+	  defvar = PHI_RESULT (temp);
+	  bitmap_set_bit (visited, SSA_NAME_VERSION (defvar));
+	}
       else
 	defvar = gimple_vdef (temp);
-      defvar_def = temp;
-      temp = NULL;
+      auto_vec<gimple *, 10> defs;
+      gimple *phi_def = NULL;
       FOR_EACH_IMM_USE_STMT (use_stmt, ui, defvar)
 	{
-	  cnt++;
-
-	  /* If we ever reach our DSE candidate stmt again fail.  We
-	     cannot handle dead stores in loops.  */
-	  if (use_stmt == stmt)
+	  /* Limit stmt walking.  */
+	  if (++cnt > PARAM_VALUE (PARAM_DSE_MAX_ALIAS_QUERIES_PER_STORE))
 	    {
 	      fail = true;
 	      BREAK_FROM_IMM_USE_STMT (ui);
 	    }
+
+	  /* We have visited ourselves already so ignore STMT for the
+	     purpose of chaining.  */
+	  if (use_stmt == stmt)
+	    ;
 	  /* In simple cases we can look through PHI nodes, but we
 	     have to be careful with loops and with memory references
 	     containing operands that are also operands of PHI nodes.
 	     See gcc.c-torture/execute/20051110-*.c.  */
 	  else if (gimple_code (use_stmt) == GIMPLE_PHI)
 	    {
-	      if (temp
-		  /* Make sure we are not in a loop latch block.  */
-		  || gimple_bb (stmt) == gimple_bb (use_stmt)
-		  || dominated_by_p (CDI_DOMINATORS,
-				     gimple_bb (stmt), gimple_bb (use_stmt))
-		  /* We can look through PHIs to regions post-dominating
-		     the DSE candidate stmt.  */
-		  || !dominated_by_p (CDI_POST_DOMINATORS,
-				      gimple_bb (stmt), gimple_bb (use_stmt)))
+	      /* If we already visited this PHI ignore it for further
+		 processing.  */
+	      if (!bitmap_bit_p (visited,
+				 SSA_NAME_VERSION (PHI_RESULT (use_stmt))))
 		{
-		  fail = true;
-		  BREAK_FROM_IMM_USE_STMT (ui);
+		  defs.safe_push (use_stmt);
+		  phi_def = use_stmt;
 		}
-	      /* Do not consider the PHI as use if it dominates the
-	         stmt defining the virtual operand we are processing,
-		 we have processed it already in this case.  */
-	      if (gimple_bb (defvar_def) != gimple_bb (use_stmt)
-		  && !dominated_by_p (CDI_DOMINATORS,
-				      gimple_bb (defvar_def),
-				      gimple_bb (use_stmt)))
-		temp = use_stmt;
 	    }
 	  /* If the statement is a use the store is not dead.  */
 	  else if (ref_maybe_used_by_stmt_p (use_stmt, ref))
@@ -581,42 +632,31 @@
 	      /* Handle common cases where we can easily build an ao_ref
 		 structure for USE_STMT and in doing so we find that the
 		 references hit non-live bytes and thus can be ignored.  */
-	      if (byte_tracking_enabled && (!gimple_vdef (use_stmt) || !temp))
+	      if (byte_tracking_enabled
+		  && is_gimple_assign (use_stmt))
 		{
-		  if (is_gimple_assign (use_stmt))
+		  ao_ref use_ref;
+		  ao_ref_init (&use_ref, gimple_assign_rhs1 (use_stmt));
+		  if (valid_ao_ref_for_dse (&use_ref)
+		      && use_ref.base == ref->base
+		      && known_eq (use_ref.size, use_ref.max_size)
+		      && !live_bytes_read (use_ref, ref, live_bytes))
 		    {
-		      /* Other cases were noted as non-aliasing by
-			 the call to ref_maybe_used_by_stmt_p.  */
-		      ao_ref use_ref;
-		      ao_ref_init (&use_ref, gimple_assign_rhs1 (use_stmt));
-		      if (valid_ao_ref_for_dse (&use_ref)
-			  && use_ref.base == ref->base
-			  && use_ref.size == use_ref.max_size
-			  && !live_bytes_read (use_ref, ref, live_bytes))
-			{
-			  /* If this statement has a VDEF, then it is the
-			     first store we have seen, so walk through it.  */
-			  if (gimple_vdef (use_stmt))
-			    temp = use_stmt;
-			  continue;
-			}
+		      /* If this is a store, remember it as we possibly
+			 need to walk the defs uses.  */
+		      if (gimple_vdef (use_stmt))
+			defs.safe_push (use_stmt);
+		      continue;
 		    }
 		}
 
 	      fail = true;
 	      BREAK_FROM_IMM_USE_STMT (ui);
 	    }
-	  /* If this is a store, remember it or bail out if we have
-	     multiple ones (the will be in different CFG parts then).  */
+	  /* If this is a store, remember it as we possibly need to walk the
+	     defs uses.  */
 	  else if (gimple_vdef (use_stmt))
-	    {
-	      if (temp)
-		{
-		  fail = true;
-		  BREAK_FROM_IMM_USE_STMT (ui);
-		}
-	      temp = use_stmt;
-	    }
+	    defs.safe_push (use_stmt);
 	}
 
       if (fail)
@@ -631,25 +671,77 @@
       /* If we didn't find any definition this means the store is dead
          if it isn't a store to global reachable memory.  In this case
 	 just pretend the stmt makes itself dead.  Otherwise fail.  */
-      if (!temp)
+      if (defs.is_empty ())
 	{
 	  if (ref_may_alias_global_p (ref))
 	    return DSE_STORE_LIVE;
 
-	  temp = stmt;
-	  break;
+	  if (by_clobber_p)
+	    *by_clobber_p = false;
+	  return DSE_STORE_DEAD;
 	}
 
-      if (byte_tracking_enabled && temp)
-	clear_bytes_written_by (live_bytes, temp, ref);
+      /* Process defs and remove those we need not process further.  */
+      for (unsigned i = 0; i < defs.length ();)
+	{
+	  gimple *def = defs[i];
+	  gimple *use_stmt;
+	  use_operand_p use_p;
+	  /* If the path to check starts with a kill we do not need to
+	     process it further.
+	     ???  With byte tracking we need only kill the bytes currently
+	     live.  */
+	  if (stmt_kills_ref_p (def, ref))
+	    {
+	      if (by_clobber_p && !gimple_clobber_p (def))
+		*by_clobber_p = false;
+	      defs.unordered_remove (i);
+	    }
+	  /* In addition to kills we can remove defs whose only use
+	     is another def in defs.  That can only ever be PHIs of which
+	     we track a single for simplicity reasons (we fail for multiple
+	     PHIs anyways).  We can also ignore defs that feed only into
+	     already visited PHIs.  */
+	  else if (gimple_code (def) != GIMPLE_PHI
+		   && single_imm_use (gimple_vdef (def), &use_p, &use_stmt)
+		   && (use_stmt == phi_def
+		       || (gimple_code (use_stmt) == GIMPLE_PHI
+			   && bitmap_bit_p (visited,
+					    SSA_NAME_VERSION
+					      (PHI_RESULT (use_stmt))))))
+	    defs.unordered_remove (i);
+	  else
+	    ++i;
+	}
+
+      /* If all defs kill the ref we are done.  */
+      if (defs.is_empty ())
+	return DSE_STORE_DEAD;
+      /* If more than one def survives fail.  */
+      if (defs.length () > 1)
+	{
+	  /* STMT might be partially dead and we may be able to reduce
+	     how many memory locations it stores into.  */
+	  if (byte_tracking_enabled && !gimple_clobber_p (stmt))
+	    return DSE_STORE_MAYBE_PARTIAL_DEAD;
+	  return DSE_STORE_LIVE;
+	}
+      temp = defs[0];
+
+      /* Track partial kills.  */
+      if (byte_tracking_enabled)
+	{
+	  clear_bytes_written_by (live_bytes, temp, ref);
+	  if (bitmap_empty_p (live_bytes))
+	    {
+	      if (by_clobber_p && !gimple_clobber_p (temp))
+		*by_clobber_p = false;
+	      return DSE_STORE_DEAD;
+	    }
+	}
     }
-  /* Continue walking until we reach a full kill as a single statement
-     or there are no more live bytes.  */
-  while (!stmt_kills_ref_p (temp, ref)
-	 && !(byte_tracking_enabled && bitmap_empty_p (live_bytes)));
-
-  *use_stmt = temp;
-  return DSE_STORE_DEAD;
+  /* Continue walking until there are no more live bytes.  */
+  while (1);
 }
 
 
@@ -779,11 +871,10 @@
 		  return;
 		}
 
-	      gimple *use_stmt;
 	      enum dse_store_status store_status;
 	      m_byte_tracking_enabled
 		= setup_live_bytes_from_ref (&ref, m_live_bytes);
-	      store_status = dse_classify_store (&ref, stmt, &use_stmt,
+	      store_status = dse_classify_store (&ref, stmt,
 						 m_byte_tracking_enabled,
 						 m_live_bytes);
 	      if (store_status == DSE_STORE_LIVE)
@@ -807,20 +898,20 @@
 
   if (is_gimple_assign (stmt))
     {
-      gimple *use_stmt;
+      bool by_clobber_p = false;
 
       /* Self-assignments are zombies.  */
       if (operand_equal_p (gimple_assign_rhs1 (stmt),
 			   gimple_assign_lhs (stmt), 0))
-	use_stmt = stmt;
+	;
       else
 	{
 	  m_byte_tracking_enabled
 	    = setup_live_bytes_from_ref (&ref, m_live_bytes);
 	  enum dse_store_status store_status;
-	  store_status = dse_classify_store (&ref, stmt, &use_stmt,
+	  store_status = dse_classify_store (&ref, stmt,
 					     m_byte_tracking_enabled,
-					     m_live_bytes);
+					     m_live_bytes, &by_clobber_p);
 	  if (store_status == DSE_STORE_LIVE)
 	    return;
 
@@ -836,7 +927,7 @@
       /* But only remove *this_2(D) ={v} {CLOBBER} if killed by
 	 another clobber stmt.  */
       if (gimple_clobber_p (stmt)
-	  && !gimple_clobber_p (use_stmt))
+	  && !by_clobber_p)
 	return;
 
       delete_dead_assignment (gsi);