aboutsummaryrefslogtreecommitdiff
path: root/gcc/dce.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/dce.c')
-rw-r--r--gcc/dce.c289
1 files changed, 148 insertions, 141 deletions
diff --git a/gcc/dce.c b/gcc/dce.c
index ce2edc43efb..bea9a5d544b 100644
--- a/gcc/dce.c
+++ b/gcc/dce.c
@@ -35,6 +35,7 @@ along with GCC; see the file COPYING3. If not see
#include "valtrack.h"
#include "tree-pass.h"
#include "dbgcnt.h"
+#include "rtl-iter.h"
/* -------------------------------------------------------------------------
@@ -86,6 +87,32 @@ deletable_insn_p_1 (rtx body)
}
}
+/* 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. */
@@ -110,8 +137,7 @@ deletable_insn_p (rtx_insn *insn, bool fast, bitmap arg_stores)
&& (RTL_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. */
- && ((cfun->can_delete_dead_exceptions && can_alter_cfg)
- || insn_nothrow_p (insn)))
+ && can_delete_call (insn))
return find_call_stack_args (as_a <rtx_call_insn *> (insn), false,
fast, arg_stores);
@@ -204,8 +230,7 @@ mark_insn (rtx_insn *insn, bool fast)
&& !SIBLING_CALL_P (insn)
&& (RTL_CONST_OR_PURE_CALL_P (insn)
&& !RTL_LOOPING_CONST_OR_PURE_CALL_P (insn))
- && ((cfun->can_delete_dead_exceptions && can_alter_cfg)
- || insn_nothrow_p (insn)))
+ && can_delete_call (insn))
find_call_stack_args (as_a <rtx_call_insn *> (insn), true, fast, NULL);
}
}
@@ -265,6 +290,100 @@ check_argument_store (HOST_WIDE_INT size, HOST_WIDE_INT off,
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
@@ -302,58 +421,13 @@ find_call_stack_args (rtx_call_insn *call_insn, bool do_mark, bool fast,
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);
}
@@ -369,51 +443,24 @@ find_call_stack_args (rtx_call_insn *call_insn, bool do_mark, bool fast,
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
@@ -425,61 +472,21 @@ find_call_stack_args (rtx_call_insn *call_insn, bool do_mark, bool fast,
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)