diff gcc/dce.c @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents 84e7813d76e9
children
line wrap: on
line diff
--- a/gcc/dce.c	Thu Oct 25 07:37:49 2018 +0900
+++ b/gcc/dce.c	Thu Feb 13 11:34:05 2020 +0900
@@ -1,5 +1,5 @@
 /* RTL dead code elimination.
-   Copyright (C) 2005-2018 Free Software Foundation, Inc.
+   Copyright (C) 2005-2020 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -35,6 +35,7 @@
 #include "valtrack.h"
 #include "tree-pass.h"
 #include "dbgcnt.h"
+#include "rtl-iter.h"
 
 
 /* -------------------------------------------------------------------------
@@ -86,6 +87,32 @@
     }
 }
 
+/* Don't delete calls that may throw if we cannot do so.  */
+
+static bool
+can_delete_call (rtx_insn *insn)
+{
+  if (cfun->can_delete_dead_exceptions && can_alter_cfg)
+    return true;
+  if (!insn_nothrow_p (insn))
+    return false;
+  if (can_alter_cfg)
+    return true;
+  /* If we can't alter cfg, even when the call can't throw exceptions, it
+     might have EDGE_ABNORMAL_CALL edges and so we shouldn't delete such
+     calls.  */
+  gcc_assert (CALL_P (insn));
+  if (BLOCK_FOR_INSN (insn) && BB_END (BLOCK_FOR_INSN (insn)) == insn)
+    {
+      edge e;
+      edge_iterator ei;
+
+      FOR_EACH_EDGE (e, ei, BLOCK_FOR_INSN (insn)->succs)
+	if ((e->flags & EDGE_ABNORMAL_CALL) != 0)
+	  return false;
+    }
+  return true;
+}
 
 /* Return true if INSN is a normal instruction that can be deleted by
    the DCE pass.  */
@@ -108,7 +135,9 @@
       /* We can delete dead const or pure calls as long as they do not
          infinite loop.  */
       && (RTL_CONST_OR_PURE_CALL_P (insn)
-	  && !RTL_LOOPING_CONST_OR_PURE_CALL_P (insn)))
+	  && !RTL_LOOPING_CONST_OR_PURE_CALL_P (insn))
+      /* Don't delete calls that may throw if we cannot do so.  */
+      && can_delete_call (insn))
     return find_call_stack_args (as_a <rtx_call_insn *> (insn), false,
 				 fast, arg_stores);
 
@@ -145,7 +174,6 @@
       return false;
 
     case CLOBBER:
-    case CLOBBER_HIGH:
       if (fast)
 	{
 	  /* A CLOBBER of a dead pseudo register serves no purpose.
@@ -201,7 +229,8 @@
 	  && !df_in_progress
 	  && !SIBLING_CALL_P (insn)
 	  && (RTL_CONST_OR_PURE_CALL_P (insn)
-	      && !RTL_LOOPING_CONST_OR_PURE_CALL_P (insn)))
+	      && !RTL_LOOPING_CONST_OR_PURE_CALL_P (insn))
+	  && can_delete_call (insn))
 	find_call_stack_args (as_a <rtx_call_insn *> (insn), true, fast, NULL);
     }
 }
@@ -214,10 +243,7 @@
 mark_nonreg_stores_1 (rtx dest, const_rtx pattern, void *data)
 {
   if (GET_CODE (pattern) != CLOBBER && !REG_P (dest))
-    {
-      gcc_checking_assert (GET_CODE (pattern) != CLOBBER_HIGH);
-      mark_insn ((rtx_insn *) data, true);
-    }
+    mark_insn ((rtx_insn *) data, true);
 }
 
 
@@ -228,22 +254,19 @@
 mark_nonreg_stores_2 (rtx dest, const_rtx pattern, void *data)
 {
   if (GET_CODE (pattern) != CLOBBER && !REG_P (dest))
-    {
-      gcc_checking_assert (GET_CODE (pattern) != CLOBBER_HIGH);
-      mark_insn ((rtx_insn *) data, false);
-    }
+    mark_insn ((rtx_insn *) data, false);
 }
 
 
-/* Mark INSN if BODY stores to a non-register destination.  */
+/* Mark INSN if it stores to a non-register destination.  */
 
 static void
-mark_nonreg_stores (rtx body, rtx_insn *insn, bool fast)
+mark_nonreg_stores (rtx_insn *insn, bool fast)
 {
   if (fast)
-    note_stores (body, mark_nonreg_stores_1, insn);
+    note_stores (insn, mark_nonreg_stores_1, insn);
   else
-    note_stores (body, mark_nonreg_stores_2, insn);
+    note_stores (insn, mark_nonreg_stores_2, insn);
 }
 
 
@@ -267,6 +290,100 @@
   return true;
 }
 
+/* If MEM has sp address, return 0, if it has sp + const address,
+   return that const, if it has reg address where reg is set to sp + const
+   and FAST is false, return const, otherwise return
+   INTTYPE_MINUMUM (HOST_WIDE_INT).  */
+
+static HOST_WIDE_INT
+sp_based_mem_offset (rtx_call_insn *call_insn, const_rtx mem, bool fast)
+{
+  HOST_WIDE_INT off = 0;
+  rtx addr = XEXP (mem, 0);
+  if (GET_CODE (addr) == PLUS
+      && REG_P (XEXP (addr, 0))
+      && CONST_INT_P (XEXP (addr, 1)))
+    {
+      off = INTVAL (XEXP (addr, 1));
+      addr = XEXP (addr, 0);
+    }
+  if (addr == stack_pointer_rtx)
+    return off;
+
+  if (!REG_P (addr) || fast)
+    return INTTYPE_MINIMUM (HOST_WIDE_INT);
+
+  /* If not fast, use chains to see if addr wasn't set to sp + offset.  */
+  df_ref use;
+  FOR_EACH_INSN_USE (use, call_insn)
+  if (rtx_equal_p (addr, DF_REF_REG (use)))
+    break;
+
+  if (use == NULL)
+    return INTTYPE_MINIMUM (HOST_WIDE_INT);
+
+  struct df_link *defs;
+  for (defs = DF_REF_CHAIN (use); defs; defs = defs->next)
+    if (! DF_REF_IS_ARTIFICIAL (defs->ref))
+      break;
+
+  if (defs == NULL)
+    return INTTYPE_MINIMUM (HOST_WIDE_INT);
+
+  rtx set = single_set (DF_REF_INSN (defs->ref));
+  if (!set)
+    return INTTYPE_MINIMUM (HOST_WIDE_INT);
+
+  if (GET_CODE (SET_SRC (set)) != PLUS
+      || XEXP (SET_SRC (set), 0) != stack_pointer_rtx
+      || !CONST_INT_P (XEXP (SET_SRC (set), 1)))
+    return INTTYPE_MINIMUM (HOST_WIDE_INT);
+
+  off += INTVAL (XEXP (SET_SRC (set), 1));
+  return off;
+}
+
+/* Data for check_argument_load called via note_uses.  */
+struct check_argument_load_data {
+  bitmap sp_bytes;
+  HOST_WIDE_INT min_sp_off, max_sp_off;
+  rtx_call_insn *call_insn;
+  bool fast;
+  bool load_found;
+};
+
+/* Helper function for find_call_stack_args.  Check if there are
+   any loads from the argument slots in between the const/pure call
+   and store to the argument slot, set LOAD_FOUND if any is found.  */
+
+static void
+check_argument_load (rtx *loc, void *data)
+{
+  struct check_argument_load_data *d
+    = (struct check_argument_load_data *) data;
+  subrtx_iterator::array_type array;
+  FOR_EACH_SUBRTX (iter, array, *loc, NONCONST)
+    {
+      const_rtx mem = *iter;
+      HOST_WIDE_INT size;
+      if (MEM_P (mem)
+	  && MEM_SIZE_KNOWN_P (mem)
+	  && MEM_SIZE (mem).is_constant (&size))
+	{
+	  HOST_WIDE_INT off = sp_based_mem_offset (d->call_insn, mem, d->fast);
+	  if (off != INTTYPE_MINIMUM (HOST_WIDE_INT)
+	      && off < d->max_sp_off
+	      && off + size > d->min_sp_off)
+	    for (HOST_WIDE_INT byte = MAX (off, d->min_sp_off);
+		 byte < MIN (off + size, d->max_sp_off); byte++)
+	      if (bitmap_bit_p (d->sp_bytes, byte - d->min_sp_off))
+		{
+		  d->load_found = true;
+		  return;
+		}
+	}
+    }
+}
 
 /* Try to find all stack stores of CALL_INSN arguments if
    ACCUMULATE_OUTGOING_ARGS.  If all stack stores have been found
@@ -304,58 +421,13 @@
     if (GET_CODE (XEXP (p, 0)) == USE
 	&& MEM_P (XEXP (XEXP (p, 0), 0)))
       {
-	rtx mem = XEXP (XEXP (p, 0), 0), addr;
-	HOST_WIDE_INT off = 0, size;
+	rtx mem = XEXP (XEXP (p, 0), 0);
+	HOST_WIDE_INT size;
 	if (!MEM_SIZE_KNOWN_P (mem) || !MEM_SIZE (mem).is_constant (&size))
 	  return false;
-	addr = XEXP (mem, 0);
-	if (GET_CODE (addr) == PLUS
-	    && REG_P (XEXP (addr, 0))
-	    && CONST_INT_P (XEXP (addr, 1)))
-	  {
-	    off = INTVAL (XEXP (addr, 1));
-	    addr = XEXP (addr, 0);
-	  }
-	if (addr != stack_pointer_rtx)
-	  {
-	    if (!REG_P (addr))
-	      return false;
-	    /* If not fast, use chains to see if addr wasn't set to
-	       sp + offset.  */
-	    if (!fast)
-	      {
-		df_ref use;
-		struct df_link *defs;
-		rtx set;
-
-		FOR_EACH_INSN_USE (use, call_insn)
-		  if (rtx_equal_p (addr, DF_REF_REG (use)))
-		    break;
-
-		if (use == NULL)
-		  return false;
-
-		for (defs = DF_REF_CHAIN (use); defs; defs = defs->next)
-		  if (! DF_REF_IS_ARTIFICIAL (defs->ref))
-		    break;
-
-		if (defs == NULL)
-		  return false;
-
-		set = single_set (DF_REF_INSN (defs->ref));
-		if (!set)
-		  return false;
-
-		if (GET_CODE (SET_SRC (set)) != PLUS
-		    || XEXP (SET_SRC (set), 0) != stack_pointer_rtx
-		    || !CONST_INT_P (XEXP (SET_SRC (set), 1)))
-		  return false;
-
-		off += INTVAL (XEXP (SET_SRC (set), 1));
-	      }
-	    else
-	      return false;
-	  }
+	HOST_WIDE_INT off = sp_based_mem_offset (call_insn, mem, fast);
+	if (off == INTTYPE_MINIMUM (HOST_WIDE_INT))
+	  return false;
 	min_sp_off = MIN (min_sp_off, off);
 	max_sp_off = MAX (max_sp_off, off + size);
       }
@@ -371,51 +443,24 @@
     if (GET_CODE (XEXP (p, 0)) == USE
 	&& MEM_P (XEXP (XEXP (p, 0), 0)))
       {
-	rtx mem = XEXP (XEXP (p, 0), 0), addr;
-	HOST_WIDE_INT off = 0, byte, size;
+	rtx mem = XEXP (XEXP (p, 0), 0);
 	/* Checked in the previous iteration.  */
-	size = MEM_SIZE (mem).to_constant ();
-	addr = XEXP (mem, 0);
-	if (GET_CODE (addr) == PLUS
-	    && REG_P (XEXP (addr, 0))
-	    && CONST_INT_P (XEXP (addr, 1)))
-	  {
-	    off = INTVAL (XEXP (addr, 1));
-	    addr = XEXP (addr, 0);
-	  }
-	if (addr != stack_pointer_rtx)
-	  {
-	    df_ref use;
-	    struct df_link *defs;
-	    rtx set;
-
-	    FOR_EACH_INSN_USE (use, call_insn)
-	      if (rtx_equal_p (addr, DF_REF_REG (use)))
-		break;
-
-	    for (defs = DF_REF_CHAIN (use); defs; defs = defs->next)
-	      if (! DF_REF_IS_ARTIFICIAL (defs->ref))
-		break;
-
-	    set = single_set (DF_REF_INSN (defs->ref));
-	    off += INTVAL (XEXP (SET_SRC (set), 1));
-	  }
-	for (byte = off; byte < off + size; byte++)
-	  {
-	    if (!bitmap_set_bit (sp_bytes, byte - min_sp_off))
-	      gcc_unreachable ();
-	  }
+	HOST_WIDE_INT size = MEM_SIZE (mem).to_constant ();
+	HOST_WIDE_INT off = sp_based_mem_offset (call_insn, mem, fast);
+	gcc_checking_assert (off != INTTYPE_MINIMUM (HOST_WIDE_INT));
+	for (HOST_WIDE_INT byte = off; byte < off + size; byte++)
+	  if (!bitmap_set_bit (sp_bytes, byte - min_sp_off))
+	    gcc_unreachable ();
       }
 
   /* Walk backwards, looking for argument stores.  The search stops
-     when seeing another call, sp adjustment or memory store other than
-     argument store.  */
+     when seeing another call, sp adjustment, memory store other than
+     argument store or a read from an argument stack slot.  */
+  struct check_argument_load_data data
+    = { sp_bytes, min_sp_off, max_sp_off, call_insn, fast, false };
   ret = false;
   for (insn = PREV_INSN (call_insn); insn; insn = prev_insn)
     {
-      rtx set, mem, addr;
-      HOST_WIDE_INT off;
-
       if (insn == BB_HEAD (BLOCK_FOR_INSN (call_insn)))
 	prev_insn = NULL;
       else
@@ -427,61 +472,21 @@
       if (!NONDEBUG_INSN_P (insn))
 	continue;
 
-      set = single_set (insn);
+      rtx set = single_set (insn);
       if (!set || SET_DEST (set) == stack_pointer_rtx)
 	break;
 
+      note_uses (&PATTERN (insn), check_argument_load, &data);
+      if (data.load_found)
+	break;
+
       if (!MEM_P (SET_DEST (set)))
 	continue;
 
-      mem = SET_DEST (set);
-      addr = XEXP (mem, 0);
-      off = 0;
-      if (GET_CODE (addr) == PLUS
-	  && REG_P (XEXP (addr, 0))
-	  && CONST_INT_P (XEXP (addr, 1)))
-	{
-	  off = INTVAL (XEXP (addr, 1));
-	  addr = XEXP (addr, 0);
-	}
-      if (addr != stack_pointer_rtx)
-	{
-	  if (!REG_P (addr))
-	    break;
-	  if (!fast)
-	    {
-	      df_ref use;
-	      struct df_link *defs;
-	      rtx set;
-
-	      FOR_EACH_INSN_USE (use, insn)
-		if (rtx_equal_p (addr, DF_REF_REG (use)))
-		  break;
-
-	      if (use == NULL)
-		break;
-
-	      for (defs = DF_REF_CHAIN (use); defs; defs = defs->next)
-		if (! DF_REF_IS_ARTIFICIAL (defs->ref))
-		  break;
-
-	      if (defs == NULL)
-		break;
-
-	      set = single_set (DF_REF_INSN (defs->ref));
-	      if (!set)
-		break;
-
-	      if (GET_CODE (SET_SRC (set)) != PLUS
-		  || XEXP (SET_SRC (set), 0) != stack_pointer_rtx
-		  || !CONST_INT_P (XEXP (SET_SRC (set), 1)))
-		break;
-
-	      off += INTVAL (XEXP (SET_SRC (set), 1));
-	    }
-	  else
-	    break;
-	}
+      rtx mem = SET_DEST (set);
+      HOST_WIDE_INT off = sp_based_mem_offset (call_insn, mem, fast);
+      if (off == INTTYPE_MINIMUM (HOST_WIDE_INT))
+	break;
 
       HOST_WIDE_INT size;
       if (!MEM_SIZE_KNOWN_P (mem)
@@ -579,7 +584,12 @@
 	  rtx turn_into_use = NULL_RTX;
 
 	  /* Always delete no-op moves.  */
-	  if (noop_move_p (insn))
+	  if (noop_move_p (insn)
+	      /* Unless the no-op move can throw and we are not allowed
+		 to alter cfg.  */
+	      && (!cfun->can_throw_non_call_exceptions
+		  || (cfun->can_delete_dead_exceptions && can_alter_cfg)
+		  || insn_nothrow_p (insn)))
 	    {
 	      if (RTX_FRAME_RELATED_P (insn))
 		turn_into_use
@@ -622,12 +632,6 @@
 	     for the destination regs in order to avoid dangling notes.  */
 	  remove_reg_equal_equiv_notes_for_defs (insn);
 
-	  /* If a pure or const call is deleted, this may make the cfg
-	     have unreachable blocks.  We rememeber this and call
-	     delete_unreachable_blocks at the end.  */
-	  if (CALL_P (insn))
-	    must_clean = true;
-
 	  if (turn_into_use)
 	    {
 	      /* Don't remove frame related noop moves if they cary
@@ -640,12 +644,16 @@
 	    }
 	  else
 	    /* Now delete the insn.  */
-	    delete_insn_and_edges (insn);
+	    must_clean |= delete_insn_and_edges (insn);
 	}
 
   /* Deleted a pure or const call.  */
   if (must_clean)
-    delete_unreachable_blocks ();
+    {
+      gcc_assert (can_alter_cfg);
+      delete_unreachable_blocks ();
+      free_dominance_info (CDI_DOMINATORS);
+    }
 }
 
 
@@ -676,7 +684,7 @@
 	    if (arg_stores && bitmap_bit_p (arg_stores, INSN_UID (insn)))
 	      continue;
 	    if (deletable_insn_p (insn, fast, arg_stores))
-	      mark_nonreg_stores (PATTERN (insn), insn, fast);
+	      mark_nonreg_stores (insn, fast);
 	    else
 	      mark_insn (insn, fast);
 	  }